Tenga más control sobre la lógica de autenticación de su aplicación Next.js a través de una implementación de autenticación personalizada basada en JWT.

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 nodos.

instagram viewer
npm install jose universal-cookie

José es un módulo de JavaScript que proporciona un conjunto de utilidades para trabajar con tokens web JSON mientras galleta-universal La 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 este repositorio de GitHub.

Crear la interfaz de usuario del formulario de inicio de sesión

Abre el origen/aplicación directorio, cree una nueva carpeta y asígnele un nombre acceso. Dentro de esta carpeta, agregue un nuevo página.js archivo e incluya el siguiente código.

"use client";
import { useRouter } from"next/navigation";

exportdefaultfunctionLoginPage() {
return (


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.

El usar cliente La 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 el aplicación directorio.

En este caso, se utiliza para declarar que el código en la página de inicio de sesión, en particular, el manejarEnviarla función solo se ejecuta en el cliente; de lo contrario, Next.js generará un error.

Ahora, definamos el código para el manejarEnviar funció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, el protegido ruta.

Definir el punto final de la API de inicio de sesión

Dentro de origen/aplicación directorio, cree una nueva carpeta y asígnele un nombre API. Dentro de esta carpeta, agregue un nuevo iniciar sesión/ruta.js archivo e incluya el siguiente código.

import { SignJWT } from"jose";
import { NextResponse } from"next/server";
import { getJwtSecretKey } from"@/libs/auth";

exportasyncfunctionPOST(request) {
const body = await request.json();
if (body.username "admin" && body.password "admin") {
const token = awaitnew 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: "/",
});
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á el jwtVerificar función proporcionada por el José módulo para verificar los tokens JWT pasados ​​con solicitudes HTTP posteriores.

En el src directorio, cree un nuevo libs/auth.js archivo e incluya el siguiente código.

import { jwtVerify } from"jose";

exportfunctiongetJwtSecretKey() {
const secret = process.env.NEXT_PUBLIC_JWT_SECRET_KEY;
if (!secret) {
thrownewError("JWT Secret key is not matched");
}
returnnew TextEncoder().encode(secret);
}

exportasyncfunctionverifyJwtToken(token) {
try {
const { payload } = await jwtVerify(token, getJwtSecretKey());
return payload;
} catch (error) {
returnnull;
}
}

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 .env archivo 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 nuevo protegido/página.js archivo en el origen/aplicación directorio. Dentro de este archivo, agregue el siguiente código.

exportdefaultfunctionProtectedPage() {
return<h1>Very protected pageh1>;
}

Cree un enlace para administrar el estado de autenticación

Crea una nueva carpeta en el src directorio y ponerle un nombre manos. Dentro de esta carpeta agregue un nuevo utilizarAuth/index.js archivo e incluya el siguiente código.

"use client" ;
import React from"react";
import Cookies from"universal-cookie";
import { verifyJwtToken } from"@/libs/auth";

exportfunctionuseAuth() {
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 el verificarJwtToken y luego establece los detalles del usuario autenticado en la autenticación estado.

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 el hogar ruta basada en el estado de autenticación de un usuario.

Un enfoque alternativo que podría considerar es el manejo gestión del estado usando Redux Toolkit o empleando un 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 el aplicación/página.js archivo, 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";
exportdefaultfunctionHome() {
const auth = useAuth();
return<>

Public Home Page</h1>

El código anterior utiliza el usarAuth gancho para administrar el estado de autenticación. Al hacerlo, muestra condicionalmente una página de inicio pública con un enlace al acceso ruta de la página cuando el usuario no está autenticado y muestra un párrafo para un usuario autenticado.

Agregue un middleware para imponer el acceso autorizado a rutas protegidas

En el src directorio, cree un nuevo middleware.js archivo 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));

exportasyncfunctionmiddleware(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();

}
exportconst 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 una estrategia de seguridad integral. enfoque que aborda de manera integral posibles lagunas y vulnerabilidades de seguridad para garantizar una proteccion.