Quero abordar um assunto muito específico relacionado a implementação de uma página de login personalizada com Credentials Sign in utilizando NextAuth com a estrutura App Router do Next.js.
Algo que ficou muito claro para mim é que o NextAuth foi construído para funcionar com a estrutura Pages Router do Next.js e sua compatibilidade com a App Router definitivamente não é a ideal.
Entendendo a lógica de funcionamento
Ao optar por utilizar uma página de login personalizada é preciso ficar atendo aos requisitos para tal. Afinal, aqui estamos optando por personalizar a experiência de Login de nossos usuários, o que de fato pode ser algo extremamente positivo, porém, também pode ser um tanto trabalhoso.
Quero focar na implementação do login com credenciais, onde defino uma lógica personalizada para verificar o usuário.
Vamos pensar no cenário onde necessitamos validar o nome e senha do usuário. A documentação do NextAuth nos orienta a criar um formulário relativamente simples, porém, com uma informação crucial: csrfToken.
<form method="post" action="/api/auth/callback/credentials">
// Este campo recebe o csrfToken e é obrigatório, porém, é um campo oculto.
<input name="csrfToken" type="hidden" defaultValue={ csrfToken } />
<input id="email" name="email" type="email" placeholder="Digite seu Email" required />
<input id="password" name="password" type="password" placeholder="Digite sua Senha" required />
<button type="submit">Entrar</button>
</form>
O problema
Aqui reside o maior problema! Como obter o csrfToken? Bom, o NextAuth nos oferece uma função para obtê-lo: getCsrfToken()
, conforme podemos ver na documentação. Essa função funciona muito bem na Pages Router do Next.js, porém, ela simplesmente não funciona na App Router. Não funciona pois ela retorna um token diferente do realmente definido pela aplicação.
Esse token é armazenado como um cookie em nosso navegador e é muito fácil fazer essa comparação.
Como obter o CSRF Token da forma correta
O Next.js nos oferece uma função para obter os cookies do navegador.
import { cookies } from 'next/headers'
export default async function SignIn() {
const cookieStore = cookies()
// Observe que estou utilizando uma variável de ambiente
const csrfToken = cookieStore.get( `${ process.env.NEXTAUTH_CSRF_TOKEN }` )?.value.split( '|' )[0]
return '...'
}
A explicação do código é relativamente simples, obtenho o cookie informando o seu nome e recupero a parte dele que realmente necessito.
O ponto principal aqui é que em desenvolvimento o nome do cookie é next-auth.csrf-token e em produção __Host-next-auth.csrf-token, por esse motivo defini uma variável de ambiente para informar o nome correto do cookie.
Essa é uma implementação simples que resolve o problema.
import { cookies } from 'next/headers'
export default async function SignIn() {
const cookieStore = cookies()
const csrfToken = cookieStore.get( `${ process.env.NEXTAUTH_CSRF_TOKEN }` )?.value.split( '|' )[0]
return (
<form method="post" action="/api/auth/callback/credentials">
<input name="csrfToken" type="hidden" defaultValue={ csrfToken } />
<input id="email" name="email" type="email" placeholder="Digite seu Email" required />
<input id="password" name="password" type="password" placeholder="Digite sua Senha" required />
<button type="submit">Entrar</button>
</form>
)
}
Top comments (0)