How to handle Authentication in React

May 09, 2017

Here’s a little snippet on how to do it.

// auth.js

import { API_ROOT } from 'constants';
import axios, { get, post } from 'axios';
import { push } from 'react-router-redux';
import jwtDecode from 'jwt-decode';
import { isJWT } from 'utils/common';
// ------------------------------------
// Constants
// ------------------------------------
export const SIGIN_REQUEST = 'SIGIN_REQUEST';
export const SIGIN_SUCCESS = 'SIGIN_SUCCESS';
export const SIGIN_FAILURE = 'SIGIN_FAILURE';

export const AUTH_USER = 'AUTH_USER';
export const UNAUTH_USER = 'UNAUTH_USER';

// ------------------------------------
// Actions
// ------------------------------------

export const initAuthState = () => {
  const token = localStorage.getItem('token');
  return (dispatch, getState) => {
    dispatch(authUser(token));
  };
};

export const authUser = token => {
  // Lets intercept all ajax calls and include token in it.
  axios.defaults.headers.token = token;
  // Store in the localStorage
  localStorage.setItem('token', token);

  return (dispatch, getState) => {
    dispatch({
      type: AUTH_USER,
      payload: {
        token,
      },
    });
    dispatch({ type: SIGIN_REQUEST });
  };
};

export const signoutUser = () => {
  localStorage.removeItem('token');

  axios.defaults.headers.token = null;

  return (dispatch, getState) => {
    dispatch(push('/login'));
    dispatch({ type: UNAUTH_USER });
  };
};

export const signinUser = data => {
  return (dispatch, getState) => {
    const url = `${API_ROOT}/login`;
    dispatch({ type: SIGIN_REQUEST });
    const request = post(url, data)
      .then(response => {
        dispatch({
          type: SIGIN_SUCCESS,
          payload: response.data,
        });
        dispatch(authUser(response.data.token));
      })
      .catch(err => {
        dispatch({
          type: SIGIN_FAILURE,
          payload: err.response.data,
        });
      });
  };
};

export const actions = {
  authUser,
  signinUser,
  signoutUser,
};
// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [SIGIN_REQUEST]: (state, action) => {
    return { ...state, loading: true, errorMsg: '' };
  },
  [SIGIN_SUCCESS]: (state, action) => {
    return { ...state, loading: false, errorMsg: '' };
  },
  [SIGIN_FAILURE]: (state, action) => {
    const errorMsg = action.payload.message || 'Invalid Credentials';
    return { ...state, loading: false, errorMsg };
  },
  [AUTH_USER]: (state, action) => {
    const { token } = action.payload;
    return { ...state, token, role, id, authenticated: true, loading: false };
  },
  [UNAUTH_USER]: (state, action) => {
    return {
      ...state,
      token: null,
      role: null,
      authenticated: false,
      loading: false,
    };
  },
};

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
  loading: false,
  errorMsg: '',
  role: '',
  token: '',
  authenticated: false,
};
export default function authReducer(state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type];
  return handler ? handler(state, action) : state;
}
// reducer.js
export const makeRootReducer = asyncReducers => {
  return combineReducers({
    auth: authReducer,
    ...asyncReducers,
  });
};

👋 I'm Ashik Nesin • Sr. Frontend Engineer @Chargebee 👨‍💻• I write about modern Javascript & building web apps

© 2021 Nesin Technologies LLP