La autenticación de tokens es una estrategia popular que se utiliza para proteger las aplicaciones web y móviles del acceso no autorizado. En Next.js, puede utilizar las funciones de autenticación proporcionadas por Next-auth.
Alternativamente, puede optar por desarrollar un sistema de autenticación personalizado basado en tokens utilizando JSON Web Tokens (JWT). Al hacerlo, se asegura de tener más control sobre la lógica de autenticación; Básicamente, personalizar el sistema para que coincida con precisión con los requisitos de su proyecto.
Configurar un proyecto Next.js
Para comenzar, instale Next.js ejecutando el siguiente comando en su terminal.
npx create-next-app@latest next-auth-jwt --experimental-app Esta guía utilizará Next.js 13, que incluye el directorio de la aplicación.
A continuación, instale estas dependencias en su proyecto usando npm, el Administrador de paquetes de nodo.
npm install jose universal-cookie Josées un módulo de JavaScript que proporciona un conjunto de utilidades para trabajar con tokens web JSON mientrasgalleta-universalLa dependencia proporciona una forma sencilla de trabajar con las cookies del navegador tanto en el lado del cliente como en el del servidor.
Puedes encontrar el código de este proyecto en esterepositorio de GitHub.
Crear la interfaz de usuario del formulario de inicio de sesión
Abre elorigen/aplicacióndirectorio, cree una nueva carpeta y asígnele un nombreacceso. Dentro de esta carpeta, agregue un nuevopágina.jsarchivo e incluya el siguiente código.
"use client";
import { useRouter } from "next/navigation";export default function LoginPage() {
return (
<form onSubmit={handleSubmit}>
<label>
Username:
<input type="text" name="username" />
</label>
<label>
Password:
<input type="password" name="password" />
</label>
<button type="submit">Login</button>
</form>
);
}
El código anterior crea un componente funcional de la página de inicio de sesión que generará un formulario de inicio de sesión simple en el navegador para permitir a los usuarios ingresar un nombre de usuario y una contraseña.
Elusar clienteLa declaración en el código garantiza que se declare un límite entre el código de solo servidor y el de solo cliente en elaplicacióndirectorio.
En este caso, se utiliza para declarar que el código en la página de inicio de sesión, en particular, elmanejarEnviar la función solo se ejecuta en el cliente; de lo contrario, Next.js generará un error.
Ahora, definamos el código para elmanejarEnviarfunción. Dentro del componente funcional, agregue el siguiente código.
const router = useRouter();const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData(event.target);
const username = formData.get("username");
const password = formData.get("password");
const res = await fetch("/api/login", {
method: "POST",
body: JSON.stringify({ username, password }),
});
const { success } = await res.json();
if (success) {
router.push("/protected");
router.refresh();
} else {
alert("Login failed");
}
};
Para administrar la lógica de autenticación de inicio de sesión, esta función captura las credenciales del usuario del formulario de inicio de sesión. Luego envía una solicitud POST a un punto final API pasando los detalles del usuario para su verificación.
Si las credenciales son válidas, lo que indica que el proceso de inicio de sesión fue exitoso: la API devuelve un estado exitoso en la respuesta. La función del controlador luego usará el enrutador de Next.js para llevar al usuario a una URL específica; en este caso, elprotegidoruta.
Definir el punto final de la API de inicio de sesión
Dentro delorigen/aplicacióndirectorio, cree una nueva carpeta y asígnele un nombreAPI. Dentro de esta carpeta, agregue un nuevoiniciar sesión/ruta.jsarchivo e incluya el siguiente código.
import { SignJWT } from "jose";
import { NextResponse } from "next/server";
import { getJwtSecretKey } from "@/libs/auth";export async function POST(request) {
const body = await request.json();
if (body.username === "admin" && body.password === "admin") {
const token = await new SignJWT({
username: body.username,
})
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("30s")
.sign(getJwtSecretKey());
const response = NextResponse.json(
{ success: true },
{ status: 200, headers: { "content-type": "application/json" } }
);
response.cookies.set({
name: "token",
value: token,
path: "https://www.makeuseof.com/",
});
return response;
}
return NextResponse.json({ success: false });
}
La tarea principal de esta API es verificar las credenciales de inicio de sesión pasadas en las solicitudes POST utilizando datos simulados.
Tras la verificación exitosa, genera un token JWT cifrado asociado con los detalles del usuario autenticado. Finalmente, envía una respuesta exitosa al cliente, incluyendo el token en las cookies de respuesta; de lo contrario, devuelve una respuesta de estado de error.
Implementar lógica de verificación de tokens
El paso inicial en la autenticación del token es generar el token después de un proceso de inicio de sesión exitoso. El siguiente paso es implementar la lógica para la verificación del token.
Básicamente, utilizará eljwtVerificarfunción proporcionada por elJosémódulo para verificar los tokens JWT pasados con solicitudes HTTP posteriores.
En elsrcdirectorio, cree un nuevolibs/auth.jsarchivo e incluya el siguiente código.
import { jwtVerify } from "jose";export function getJwtSecretKey() {
const secret = process.env.NEXT_PUBLIC_JWT_SECRET_KEY;
if (!secret) {
throw new Error("JWT Secret key is not matched");
}
return new TextEncoder().encode(secret);
}
export async function verifyJwtToken(token) {
try {
const { payload } = await jwtVerify(token, getJwtSecretKey());
return payload;
} catch (error) {
return null;
}
}
La clave secreta se utiliza para firmar y verificar los tokens. Al comparar la firma del token decodificado con la firma esperada, el servidor puede verificar de manera efectiva que el token proporcionado sea válido y, en última instancia, autorizar las solicitudes de los usuarios.
Crear.envarchivo en el directorio raíz y agregue una clave secreta única de la siguiente manera:
NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key Crear una ruta protegida
Ahora, necesita crear una ruta a la que solo los usuarios autenticados puedan acceder. Para ello, cree un nuevoprotegido/página.jsarchivo en elorigen/aplicacióndirectorio. Dentro de este archivo, agregue el siguiente código.
export default function ProtectedPage() {
return <h1>Very protected page</h1>;
} Cree un enlace para administrar el estado de autenticación
Crea una nueva carpeta en elsrcdirectorio y ponerle un nombremanos. Dentro de esta carpeta agregue un nuevoutilizarAuth/index.jsarchivo e incluya el siguiente código.
"use client" ;
import React from "react";
import Cookies from "universal-cookie";
import { verifyJwtToken } from "@/libs/auth";export function useAuth() {
const [auth, setAuth] = React.useState(null);
const getVerifiedtoken = async () => {
const cookies = new Cookies();
const token = cookies.get("token") ?? null;
const verifiedToken = await verifyJwtToken(token);
setAuth(verifiedToken);
};
React.useEffect(() => {
getVerifiedtoken();
}, []);
return auth;
}
Este enlace gestiona el estado de autenticación en el lado del cliente. Obtiene y verifica la validez del token JWT presente en las cookies utilizando elverificarJwtTokeny luego establece los detalles del usuario autenticado en laautenticaciónestado.
Al hacerlo, permite que otros componentes accedan y utilicen la información del usuario autenticado. Esto es esencial para escenarios como realizar actualizaciones de la interfaz de usuario según el estado de autenticación, realizar solicitudes de API posteriores o representar contenido diferente según los roles de los usuarios.
En este caso, utilizará el enlace para representar contenido diferente en elhogarruta basada en el estado de autenticación de un usuario.
Un enfoque alternativo que podría considerar es manejar la gestión estatal utilizando Redux Toolkit o empleando una herramienta de gestión estatal como Jotai. Este enfoque garantiza que los componentes puedan obtener acceso global al estado de autenticación o a cualquier otro estado definido.
Adelante, abre elaplicación/página.jsarchivo, elimine el código repetitivo Next.js y agregue el siguiente código.
"use client" ;import { useAuth } from "@/hooks/useAuth";
import Link from "next/link";
export default function Home() {
const auth = useAuth();
return <>
<h1>Public Home Page</h1>
<header>
<nav>
{auth ? (
<p>logged in</p>
) : (
<Link href="/login">Login</Link>
)}
</nav>
</header>
</>
}
El código anterior utiliza elusarAuthgancho para administrar el estado de autenticación. Al hacerlo, muestra condicionalmente una página de inicio pública con un enlace alaccesoruta de la página cuando el usuario no está autenticado y muestra un párrafo para un usuario autenticado.
En elsrcdirectorio, cree un nuevomiddleware.jsarchivo y agregue el código a continuación.
import { NextResponse } from "next/server";
import { verifyJwtToken } from "@/libs/auth";const AUTH_PAGES = ["/login"];
const isAuthPages = (url) => AUTH_PAGES.some((page) => page.startsWith(url));
export async function middleware(request) {
const { url, nextUrl, cookies } = request;
const { value: token } = cookies.get("token") ?? { value: null };
const hasVerifiedToken = token && (await verifyJwtToken(token));
const isAuthPageRequested = isAuthPages(nextUrl.pathname);
if (isAuthPageRequested) {
if (!hasVerifiedToken) {
const response = NextResponse.next();
response.cookies.delete("token");
return response;
}
const response = NextResponse.redirect(new URL(`/`, url));
return response;
}
if (!hasVerifiedToken) {
const searchParams = new URLSearchParams(nextUrl.searchParams);
searchParams.set("next", nextUrl.pathname);
const response = NextResponse.redirect(
new URL(`/login?${searchParams}`, url)
);
response.cookies.delete("token");
return response;
}
return NextResponse.next();
}
export const config = { matcher: ["/login", "/protected/:path*"] };
Este código de middleware actúa como guardia. Comprueba que cuando los usuarios quieran acceder a páginas protegidas, estén autenticados y autorizados para acceder a las rutas, además de redirigir a los usuarios no autorizados a la página de inicio de sesión.
Protección de aplicaciones Next.js
La autenticación de token es un mecanismo de seguridad eficaz. Sin embargo, no es la única estrategia disponible para proteger sus aplicaciones del acceso no autorizado.
Para fortalecer las aplicaciones frente al panorama dinámico de la ciberseguridad, es importante adoptar un enfoque de seguridad integral que aborde de manera integral las posibles lagunas y vulnerabilidades de seguridad para garantizar una protección completa.







