Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

360
Vistas
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 Respuestas
Responde la pregunta

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 Denunciar

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda