Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

366
Views
Building a dashboard in Next.js : best practices to make pages private with roles, without "flickering", using JWT authentication?

Considering that we have:

  • A backend already ready (not in Next) with authentication using JWT pattern and a home-made RBAC
  • 4 private pages only for unauthenticated people (login, signup, forgot password, reset password)
  • ~25+ private pages for the dashboard
  • 1 public page for dashboard presentation
  • Hundreds of dashboard related components (+ thousands of design system components)

Users should:

  • login before accessing the dashboard
  • if unauthenticated and accessing private route, should be redirected to /login without flickering
  • if authenticated and accessing routes for unauthenticated users, should be redirected to /profile without flickering)

My logic right now for dealing with JWT:

// lib/axios.js

import Axios from 'axios';
import { getCookie, removeCookies } from 'cookies-next';
import qs from 'qs';

export const axios = Axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  paramsSerializer: (params) =>
    qs.stringify(params, { arrayFormat: 'brackets' }),
  withCredentials: true,
});

axios.interceptors.request.use(
  (config) => {
    const token = getCookie('access_token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
      config.headers.Accept = 'application/json';
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response.status === 401) {
      // we need to implement refresh pattern
      // const refresh = await refreshToken();
      removeCookies('access_token');
    }
    return Promise.reject(error);
  }
);

// For SWR
export const fetcher = async (...args) => await axios.get(...args).data;

I've been accumulating researches about this and I found so many different answers. So far I found:

  • Provider to put in _app.js with hard-coded private routes in an array
  • Different HoC functions inside every page like withPrivate or withPublic
  • Using getServerSideProps with redirection to login inside every page
  • nextAuth but I'm not sure because it seems like it's building a backend and we've got one already
  • _middleware that can do the redirection apparently
  • It seems like it's possible to use SWR, Suspense and Error Boundaries but I'm not sure if it's adapted for this kind of cases...

Any clue about how I should do ?

about 4 years ago · Juan Pablo Isaza
2 answers
Answer question

0

If you don't use static generation and never plan to getServerSideProps works.

If you plan to use Vercel to host, _middleware could be a good option too, however, 3rd party hosting support is lagging.

Since you already have your own auth, nextAuth doesn't seem to be a good choice.

I'd recommend using an Auth context - wrapping every page with an HOC is isn't necessary anymore.

You can prevent the flickering by using a loading screen or spinner.


Here is an example of an auth context.

import { useEffect, useState, useCallback, createContext } from "react";
import { useRouter } from "next/router";

const AuthContext = createContext(null);

export const AuthProvider = ({ children }) => {
  const { asPath, push } = useRouter();
  const [user, setUser] = useState(null);
  // if you use trailing slash you'd need to add them to each route
  const isAuthRoute = ["/login", "/signup", "/forgot", "/reset"].includes(asPath);

  const redirectToLogin = useCallback(async () => {
    try {
      setUser(null);
      await push('/login');
    } catch (e) {
      console.error("Could not redirect to login");
    }
  }, [push]);

  const signOut = async () => {
    try {
      await authServiceSignout();
      await redirectToLogin();
    } catch {
      window.location.reload();
    }
  };

  const goHome = useCallback(() => push('/'), [push]);

  // check if user is logged in on every route changes and
  // redirect accordingly
  useEffect(() => {
    const getUser = async () => {
      const user = await getUserOrJWT();
      if (user) {
        setUser(user); // user details
        if (isAuthRoute) await goHome();
      } else if (!isAuthRoute) {
        await redirectToLogin();
      }
    };
    getUser();
  }, [asPath, goHome, isAuthRoute, redirectToLogin]);

  if (!user && isAuthRoute) return <>{children}</>;
  if (!user) return <>Loading or loading spinner</>;

  return (
    <AuthContext.Provider value={{ user, signOut }}>
      {children}
    </AuthContext.Provider>
  );
};

I deleted a bunch of code so it probably doesn't work direct copy and paste but you can get the idea.

isAuthRoute only needs to return true/false so another good options if your dashboard routes all start with /dashboard you can use asPath.startsWith('/dashboard') instead.

We conditionally rendered the auth context because it's not needed on public pages and it also clears the user context on logout so we don't need to worry about leaking old user data via context.

We also use local storage and the window's broadcast channel to listen for logout calls. This allows us to log the user out in every tab and window.

about 4 years ago · Juan Pablo Isaza Report

0

After a lot of tests with different techniques, I decided to go for Next.js new middleware incredible feature.

If anybody struggles with this topic like I did, here is my code:

// _middleware.js in /pages, works also with Typescript .ts
import { NextResponse } from 'next/server';
import { isAuthValid } from '@/lib/auth'

export function middleware(req) {
  if (
    req.nextUrl.pathname.startsWith('/login') ||
    req.nextUrl.pathname.startsWith('/signup') ||
    req.nextUrl.pathname.startsWith('/forgot') ||
    req.nextUrl.pathname.startsWith('/reset')
  ) {
    if (isAuthValid(req)) {
      return NextResponse.redirect(new URL('/profile', req.url));
    }
    return NextResponse.next();
  }

  // All other routes
  if (isAuthValid(req)) {
    return NextResponse.next();
  }
  return NextResponse.redirect(
    new URL(`/login?from=${req.nextUrl.pathname}`, req.url)
  );
}

Be careful though as this file name and location will change in new Next.js 12.2 version, under the name of middleware.js (or .ts) in your root folder, whether it's root or src depending on your configuration

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!