DEV Community

Nadim Chowdhury
Nadim Chowdhury

Posted on • Edited on

2

How to Implement Role-Based Authorization in Next.js Using Redux and Local Storage

Here’s a structured approach to building a role-based authorization system for your Next.js project using Redux, where access tokens and user info are stored in localStorage and retrieved on page visits.

1. Redux Slice for Authentication

This slice will handle the user authentication state, including storing the access token and user role.

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  accessToken: null,
  userInfo: null,
  isAuthenticated: false,
};

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    loginSuccess: (state, action) => {
      const { accessToken, userInfo } = action.payload;
      state.accessToken = accessToken;
      state.userInfo = userInfo;
      state.isAuthenticated = true;

      // Save token and userInfo to localStorage
      localStorage.setItem('accessToken', JSON.stringify(accessToken));
      localStorage.setItem('userInfo', JSON.stringify(userInfo));

      // Set expiry of 7 days
      const expiresAt = new Date();
      expiresAt.setDate(expiresAt.getDate() + 7);
      localStorage.setItem('expiresAt', expiresAt.toISOString());
    },
    logout: (state) => {
      state.accessToken = null;
      state.userInfo = null;
      state.isAuthenticated = false;

      // Remove from localStorage
      localStorage.removeItem('accessToken');
      localStorage.removeItem('userInfo');
      localStorage.removeItem('expiresAt');
    },
    loadUserFromStorage: (state) => {
      const accessToken = localStorage.getItem('accessToken');
      const userInfo = localStorage.getItem('userInfo');
      const expiresAt = localStorage.getItem('expiresAt');

      if (accessToken && userInfo && new Date() < new Date(expiresAt)) {
        state.accessToken = JSON.parse(accessToken);
        state.userInfo = JSON.parse(userInfo);
        state.isAuthenticated = true;
      } else {
        // Clear if token is expired
        localStorage.removeItem('accessToken');
        localStorage.removeItem('userInfo');
        localStorage.removeItem('expiresAt');
        state.isAuthenticated = false;
      }
    },
  },
});

export const { loginSuccess, logout, loadUserFromStorage } = authSlice.actions;
export default authSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

2. Auth Provider File (Context + Protected Route)

This will provide a ProtectedRoute component to wrap around pages that require authorization based on roles.

import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { loadUserFromStorage, logout } from '../store/slices/authSlice';
import { useRouter } from 'next/router';

export const AuthProvider = ({ children }) => {
  const dispatch = useDispatch();
  const { isAuthenticated, userInfo } = useSelector((state) => state.auth);

  useEffect(() => {
    dispatch(loadUserFromStorage());
  }, [dispatch]);

  return children;
};

export const ProtectedRoute = ({ children, allowedRoles }) => {
  const { isAuthenticated, userInfo } = useSelector((state) => state.auth);
  const router = useRouter();

  useEffect(() => {
    if (!isAuthenticated || (allowedRoles && !allowedRoles.includes(userInfo.role))) {
      router.push('/login');
    }
  }, [isAuthenticated, userInfo, allowedRoles, router]);

  if (!isAuthenticated || (allowedRoles && !allowedRoles.includes(userInfo.role))) {
    return null; // or a loading spinner
  }

  return children;
};
Enter fullscreen mode Exit fullscreen mode

3. Redux Store Setup

Make sure to configure the Redux store in store.js:

import { configureStore } from '@reduxjs/toolkit';
import authReducer from './slices/authSlice';

export const store = configureStore({
  reducer: {
    auth: authReducer,
  },
});
Enter fullscreen mode Exit fullscreen mode

Wrap your application in Provider in pages/_app.js:

import { Provider } from 'react-redux';
import { store } from '../store/store';
import { AuthProvider } from '../components/AuthProvider';

function MyApp({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <AuthProvider>
        <Component {...pageProps} />
      </AuthProvider>
    </Provider>
  );
}

export default MyApp;
Enter fullscreen mode Exit fullscreen mode

4. Protecting Routes in Pages

You can protect specific routes by wrapping them in the ProtectedRoute component and specifying allowed roles:

import { ProtectedRoute } from '../components/AuthProvider';

const AdminPage = () => {
  return (
    <ProtectedRoute allowedRoles={['admin']}>
      <h1>Admin Dashboard</h1>
    </ProtectedRoute>
  );
};

export default AdminPage;
Enter fullscreen mode Exit fullscreen mode

5. Login & Logout Flow

Here’s how you might implement the login and logout functions:

  • Login Action (API Call to Get Token and Set User):
import { loginSuccess } from '../store/slices/authSlice';
import { useDispatch } from 'react-redux';

const login = async (credentials) => {
  const dispatch = useDispatch();

  try {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials),
    });

    if (response.ok) {
      const data = await response.json();
      dispatch(loginSuccess({
        accessToken: data.accessToken,
        userInfo: data.user,
      }));
    }
  } catch (error) {
    console.error('Login failed', error);
  }
};
Enter fullscreen mode Exit fullscreen mode
  • Logout:
import { useDispatch } from 'react-redux';
import { logout } from '../store/slices/authSlice';

const logoutUser = () => {
  const dispatch = useDispatch();
  dispatch(logout());
};
Enter fullscreen mode Exit fullscreen mode

This setup gives you a robust structure for managing authentication with token storage in localStorage, and role-based access to routes in your Next.js project.

Support My Work ❤️

If you enjoy my content and find it valuable, consider supporting me by buying me a coffee. Your support helps me continue creating and sharing useful resources. Thank you!

Connect with Me 🌍

Let’s stay connected! You can follow me or reach out on these platforms:

🔹 YouTube – Tutorials, insights & tech content

🔹 LinkedIn – Professional updates & networking

🔹 GitHub – My open-source projects & contributions

🔹 Instagram – Behind-the-scenes & personal updates

🔹 X (formerly Twitter) – Quick thoughts & tech discussions

I’d love to hear from you—whether it’s feedback, collaboration ideas, or just a friendly hello!

Disclaimer

This content has been generated with the assistance of AI. While I strive for accuracy and quality, please verify critical information independently.

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more