Tenha mais controle sobre a lógica de autenticação do seu aplicativo Next.js por meio da implementação de autenticação personalizada baseada em JWT.
A autenticação de token é uma estratégia popular usada para proteger aplicativos da Web e móveis contra acesso não autorizado. No Next.js, você pode utilizar os recursos de autenticação fornecidos pelo Next-auth.
Alternativamente, você pode optar por desenvolver um sistema de autenticação personalizado baseado em token usando JSON Web Tokens (JWTs). Ao fazer isso, você garante mais controle sobre a lógica de autenticação; essencialmente, customizando o sistema para atender precisamente aos requisitos do seu projeto.
Configurar um projeto Next.js
Para começar, instale Next.js executando o comando abaixo em seu terminal.
npx create-next-app@latest next-auth-jwt --experimental-app
Este guia utilizará Next.js 13 que inclui o diretório do aplicativo.
A seguir, instale essas dependências em seu projeto usando npm, o gerenciador de pacotes do nó.
npm install jose universal-cookie
Jose é um módulo JavaScript que fornece um conjunto de utilitários para trabalhar com JSON Web Tokens enquanto o cookie universal dependency fornece uma maneira simples de trabalhar com cookies do navegador nos ambientes do lado do cliente e do lado do servidor.
Você pode encontrar o código deste projeto neste Repositório GitHub.
Crie a interface do usuário do formulário de login
Abra o fonte/aplicativo diretório, crie uma nova pasta e nomeie-a Conecte-se. Dentro desta pasta, adicione um novo página.js arquivo e inclua o código abaixo.
"use client";
import { useRouter } from"next/navigation";
exportdefaultfunctionLoginPage() {
return (
O código acima cria um componente funcional da página de login que renderizará um formulário de login simples no navegador para permitir que os usuários insiram um nome de usuário e uma senha.
O usar cliente instrução no código garante que um limite seja declarado entre o código somente servidor e somente cliente no aplicativo diretório.
Neste caso, é utilizado para declarar que o código da página de login, principalmente, o identificadorSubmita função só é executada no cliente; caso contrário, Next.js gerará um erro.
Agora vamos definir o código do identificadorSubmit função. Dentro do componente funcional, adicione o seguinte 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 gerenciar a lógica de autenticação de login, esta função captura as credenciais do usuário do formulário de login. Em seguida, ele envia uma solicitação POST para um endpoint da API, passando os detalhes do usuário para verificação.
Se as credenciais forem válidas, indicando que o processo de login foi bem-sucedido, a API retornará um status de sucesso na resposta. A função manipuladora usará então o roteador Next.js para navegar o usuário até um URL especificado, neste caso, o protegido rota.
Definir o ponto final da API de login
Dentro de fonte/aplicativo diretório, crie uma nova pasta e nomeie-a API. Dentro desta pasta, adicione um novo login/route.js arquivo e inclua o código abaixo.
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 });
}
A principal tarefa desta API é verificar as credenciais de login passadas nas solicitações POST usando dados simulados.
Após a verificação bem-sucedida, ele gera um token JWT criptografado associado aos detalhes do usuário autenticado. Por fim, envia uma resposta bem-sucedida ao cliente, incluindo o token nos cookies de resposta; caso contrário, ele retornará uma resposta de status de falha.
Implementar lógica de verificação de token
A etapa inicial na autenticação do token é gerar o token após um processo de login bem-sucedido. A próxima etapa é implementar a lógica para verificação de token.
Basicamente, você usará o jwtVerify função fornecida pelo Jose módulo para verificar os tokens JWT passados com solicitações HTTP subsequentes.
No fonte diretório, crie um novo libs/auth.js arquivo e inclua o código abaixo.
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;
}
}
A chave secreta é usada para assinar e verificar os tokens. Ao comparar a assinatura do token decodificado com a assinatura esperada, o servidor pode verificar efetivamente se o token fornecido é válido e, em última análise, autorizar as solicitações dos usuários.
Criar .env arquivo no diretório raiz e adicione uma chave secreta exclusiva da seguinte maneira:
NEXT_PUBLIC_JWT_SECRET_KEY=your_secret_key
Crie uma rota protegida
Agora, você precisa criar uma rota à qual apenas usuários autenticados possam ter acesso. Para isso, crie um novo protegido/página.js arquivo no fonte/aplicativo diretório. Dentro deste arquivo, adicione o seguinte código.
exportdefaultfunctionProtectedPage() {
return<h1>Very protected pageh1>;
}
Crie um gancho para gerenciar o estado de autenticação
Crie uma nova pasta no fonte diretório e nomeie-o ganchos. Dentro desta pasta adicione um novo useAuth/index.js arquivo e inclua o código abaixo.
"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 gancho gerencia o estado de autenticação no lado do cliente. Ele busca e verifica a validade do token JWT presente nos cookies usando o verificar JwtToken função e, em seguida, define os detalhes do usuário autenticado para o autenticação estado.
Ao fazer isso, permite que outros componentes acessem e utilizem as informações do usuário autenticado. Isso é essencial para cenários como fazer atualizações de UI com base no status de autenticação, fazer solicitações de API subsequentes ou renderizar conteúdo diferente com base nas funções do usuário.
Neste caso, você usará o gancho para renderizar conteúdo diferente no lar rota com base no estado de autenticação de um usuário.
Uma abordagem alternativa que você pode considerar é lidar com gerenciamento de estado usando Redux Toolkit ou empregando um ferramenta de gerenciamento de estado como Jotai. Esta abordagem garante que os componentes possam obter acesso global ao estado de autenticação ou a qualquer outro estado definido.
Vá em frente e abra o app/página.js arquivo, exclua o código Next.js padrão e adicione o código a seguir.
"use client" ;
import { useAuth } from"@/hooks/useAuth";
import Link from"next/link";
exportdefaultfunctionHome() {
const auth = useAuth();
return<>Public Home Page</h1>
O código acima utiliza o useAuth hook para gerenciar o estado de autenticação. Ao fazer isso, ele renderiza condicionalmente uma página inicial pública com um link para o Conecte-se rota de página quando o usuário não está autenticado e exibe um parágrafo para um usuário autenticado.
Adicione um middleware para impor acesso autorizado a rotas protegidas
No fonte diretório, crie um novo middleware.js arquivo e adicione o código abaixo.
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 atua como um guarda. Ele verifica se os usuários que desejam acessar páginas protegidas estão autenticados e autorizados a acessar as rotas, além de redirecionar os usuários não autorizados para a página de login.
Protegendo aplicativos Next.js
A autenticação de token é um mecanismo de segurança eficaz. No entanto, não é a única estratégia disponível para proteger as suas aplicações contra acesso não autorizado.
Para fortalecer os aplicativos contra o cenário dinâmico da segurança cibernética, é importante adotar uma estratégia de segurança abrangente abordagem que aborda holisticamente possíveis brechas e vulnerabilidades de segurança para garantir proteção.