• It’s one way to handle user authentication in a Rails API app with a React frontend.
  • To resolve this issue, we need to configure the rails application with session storage to use cookies and session.
  • Configure Cross-Origin Resource Sharing
  • Controller to check whether the user is logged in or not
  • State management in front-end with React and Redux
  • Send a request to the isLoggedIn API from the React App component (the entry point of the React application) to verify if the user is logged in. Then, store this information in the Redux store to make it accessible throughout the application.

Add middleware for cookies and session:

config.session_store :cookie_store, key: '_interslice_session’
config.middleware.use ActionDispatch::Cookies
config.middleware.use config.session_store, config.session_options

Configure Cross-Origin Resource Sharing (CORS) in config/initializers/cors.rb
Change the origin URL to the URL of localhost (later the deployed app’s domain address) and set the credentials to true.

Rails.application.config.middleware.insert_before 0, Rack::Cors do
 allow do
   origins 'http://localhost:3001'
resource '*',
     headers: :any,
     methods: [:get, :post, :put, :patch, :delete, :options, :head],
     credentials: true
 end
end

Configuration for Cookie Storage

Rails.application.config.session_store :cookie_store, {
 :key => '_your_app_name',
 :domain => :all,
 :same_site => :none,
 :secure => :true,
 :tld_length => 2
}

Create a method in the SessionsController to verify if a user exists in the session.

def create
     @user = User.new(user_params)
     if @user.save
       session[:user_id] = @user.id
       render json: {
         status: :created,
         user: @user
       }
     else
       @user.save
       render json: {
         status: 500,
         error: @user.errors.full_messages
       }
     end
   end
     def is_logged_in?
       @current_user = User.find(session[:user_id]) if session[:user_id]
       if @current_user
         render json: {
           logged_in: true,
           user: User.new(@current_user)
         }
       else
         render json: {
           logged_in: false
         }
       end
     end

With the following routes:
get ‘/logged_in’, to: ‘sessions#is_logged_in?’

App Component in React:

import React, { useEffect } from 'react';
import './App.css';
import { useSelector, useDispatch } from 'react-redux';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { fetchLoginStatus } from './actions/fetchLoginStatus';

import NavBar from './components/NavBar';
import Home from './components/Home';
import Error from './components/Error';
import Signup from './components/users/Signup';

const App = () => {
 const dispatch = useDispatch();
 const errors = useSelector((state) => state.errors);
 const user = useSelector((state) => state.user);
useEffect(() => {
   dispatch(fetchLoginStatus());
 }, [dispatch]);

 const error = () => errors.length > 0;

 return (
{error() && } } />

); }; export default App;

App component that uses Redux for state management and React Router for navigation. Here’s a brief explanation:

Imports:

  • React and hooks (useEffect, useSelector, useDispatch) for component functionality and state management.
  • react-router-dom for routing within the app.
  • Custom components (NavBar, Home, Error, and Signup) and a Redux action (fetchLoginStatus).

State Management:

  • useSelector accesses the Redux state to retrieve user.
  • useDispatch dispatches actions to update the Redux state.

Effect:

  • useEffect runs on component mount to dispatch the fetchLoginStatus action, checking if the user is logged in.

Routing:

  • Uses Router and Switch for navigation:
  • / renders the Home component.
  • /signup renders the Signup component, passing props explicitly

userReducer to store login user Data

export default (
 state = {
   isLoggedIn: false,
   id: null,
   username: '',
 },
 action
) => {
 switch (action.type) {
   case 'LOGIN_USER':
     return {
       isLoggedIn: true,
       id: action.user.id,
       username: action.user.username,
     };
   default:
     return state;
 }
};

fetchLoginStatus Action:

import axios from 'axios';
export const fetchLoginStatus = () => (dispatch) => {
 axios
   .get('http://localhost:3000/api/v1/logged_in', { withCredentials: true })
   .then((response) => {
     if (response.data.logged_in) {
       dispatch({
         type: 'LOGIN_USER',
         user: response.data.user,
       });
     }
   })
   .catch((error) =>
 // displays Error message
};

Support On Demand!

Ruby on Rails

Related Q&A