Teniendo en cuenta que tenemos:
Los usuarios deben:
/login sin parpadear/profile sin parpadear)Mi lógica en este momento para tratar con 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;He estado acumulando investigaciones sobre esto y encontré muchas respuestas diferentes. Hasta ahora encontré:
_app.js con rutas privadas codificadas en una matrizwithPrivate o withPublicgetServerSideProps con redirección para iniciar sesión dentro de cada páginanextAuth pero no estoy seguro porque parece que está construyendo un backend y ya tenemos uno_middleware que aparentemente puede hacer la redirecciónSWR , Suspense y Error Boundaries pero no estoy seguro si es adecuado para este tipo de casos...alguna pista de como debo hacer?
Si no usa la generación estática y nunca planea, getServerSideProps funciona.
Si planea usar Vercel para hospedar, _middleware podría ser una buena opción; sin embargo, el soporte de hospedaje de terceros se está quedando atrás.
Como ya tiene su propia autenticación, nextAuth no parece ser una buena opción.
Recomiendo usar un contexto Auth: ya no es necesario envolver cada página con un HOC.
Puede evitar el parpadeo utilizando una pantalla de carga o un control giratorio.
Este es un ejemplo de un contexto de autenticación.
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> ); };Eliminé un montón de código, por lo que probablemente no funcione copiar y pegar directamente, pero puedes hacerte una idea.
isAuthRoute solo necesita devolver verdadero/falso, por lo que otras buenas opciones si todas las rutas de su panel comienzan con /dashboard , puede usar asPath.startsWith('/dashboard') en su lugar.
Procesamos condicionalmente el contexto de autenticación porque no es necesario en las páginas públicas y también borra el contexto del usuario al cerrar la sesión para que no tengamos que preocuparnos por la filtración de datos antiguos del usuario a través del contexto.
También usamos el almacenamiento local y el canal de transmisión de la ventana para escuchar las llamadas de cierre de sesión. Esto nos permite cerrar la sesión del usuario en cada pestaña y ventana.
Después de muchas pruebas con diferentes técnicas, decidí optar por la nueva característica increíble de Next.js.
Si alguien tiene problemas con este tema como lo hice yo, aquí está mi código:
// _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) ); } Sin embargo, tenga cuidado ya que el nombre y la ubicación de este archivo cambiarán en la nueva versión Next.js 12.2 , bajo el nombre de middleware.js (o .ts ) en su carpeta raíz, ya sea raíz o src , según su configuración.