Alternar tema

NextAuth.js para iniciantes: guia completo de login com Credentials e gerenciamento de sessão

Easton editorial illustration: component assembly loom

Ao abrir a documentação oficial do NextAuth.js, você se depara com tantas opções de configuração que é fácil ficar perdido. Provider, Session, Adapter, JWT, Callbacks… cada termo parece familiar, mas o conjunto não faz sentido de imediato. A documentação diz que “JWT é o padrão”, mas logo depois afirma que “ao usar um banco de dados, a estratégia muda automaticamente para Session”. Afinal, qual escolher?

Na época, quase desisti e fui direto para o Clerk. Mas pensei melhor: embora o Clerk seja rápido de configurar, você começa a pagar depois de ultrapassar a cota gratuita de 10.000 usuários ativos por mês, e várias personalizações ficam limitadas. O NextAuth.js é um pouco mais difícil no início, mas é totalmente gratuito e oferece controle completo.

Este artigo não vai listar todas as opções de configuração — isso seria um sofrimento. O foco é o cenário mais comum de login com Credentials: usuário e senha, gerenciamento de sessão e integração com banco de dados. Depois que você entende esses pontos centrais, o NextAuth.js deixa de parecer tão complicado.

Entendendo os principais conceitos do NextAuth.js

O que o NextAuth.js realmente faz?

Em uma frase: NextAuth.js é um “middleware de autenticação” que gerencia “quem está conectado” e “como o estado do login é armazenado”. Ele não impõe um banco de dados nem uma interface específica; sua função é verificar a identidade do usuário e manter o estado da sessão.

Qual é a diferença em relação a Clerk e Supabase Auth? Em resumo:

  • Clerk: oferece uma interface de login bonita e um painel de gerenciamento de usuários. Você configura tudo em 30 minutos, mas paga ao ultrapassar 10.000 usuários ativos mensais.
  • Supabase Auth: se você já usa o banco de dados do Supabase, o recurso de autenticação praticamente vem de graça e a integração é muito fluida.
  • NextAuth.js: é totalmente gratuito e de código aberto, mas você precisa criar a interface de login, o cadastro de usuários e a lógica de banco de dados.

A tendência de 2025 é interessante: o NextAuth.js continua muito usado, mas soluções prontas como o Clerk estão conquistando o mercado rapidamente. Faz sentido — em vez de gastar dias escrevendo código de autenticação, muita gente prefere investir esse tempo no recurso principal do produto. Ainda assim, para projetos pessoais ou quando você precisa de controle total, vale a pena aprender NextAuth.js.

Três conceitos que você precisa entender

1. Provider (provedor): como o usuário faz login?

É o método de login. O NextAuth.js oferece mais de 50 opções; as mais comuns são:

  • Provedores OAuth: Google, GitHub, Facebook… o usuário clica e entra, sem que você precise gerenciar a senha.
  • Provedor Credentials: usuário e senha, o método mais tradicional e o foco deste artigo.

Há uma ressalva importante: o provedor Credentials é o mais flexível, mas também exige mais código de sua parte. A própria documentação oficial não o recomenda, porque toda a responsabilidade de segurança fica com você — como armazenar senhas, impedir ataques de força bruta e gerenciar sessões.

2. Session (sessão): como lembrar o usuário depois do login?

Depois de entrar uma vez, o usuário não deveria digitar a senha a cada requisição. Session é o mecanismo que “lembra o estado do login”. Há duas formas:

  • JWT Session: os dados de login são criptografados e colocados em um cookie; o servidor não precisa armazenar nada.
  • Database Session: o cookie contém apenas um ID; os dados reais do login ficam no banco de dados.

Escolher entre as duas é uma das maiores dúvidas de quem está começando com NextAuth.js. Veremos os detalhes na próxima seção.

3. Adapter (adaptador): onde os dados do usuário ficam armazenados?

Se você quer armazenar dados do usuário no banco, como email e data de cadastro, precisa de um Adapter. O NextAuth.js oferece suporte a Prisma, MongoDB, MySQL e vários outros bancos.

Mas existe um ponto essencial: o provedor Credentials não salva automaticamente os dados do usuário no banco. A documentação oficial deixa claro que, ao usar Credentials, você precisa gerenciar as contas por conta própria. O NextAuth.js apenas valida o login; ele não faz cadastro nem armazenamento.

JWT vs. Session: qual escolher?

Essa é a questão central. Na época, li mais de dez comparações e continuei indeciso, até compreender a diferença fundamental entre as duas estratégias.

JWT Session: o modelo do passaporte

Imagine uma viagem internacional: o passaporte contém sua foto, seu nome e a validade. A cada entrada no país, a imigração consulta o documento sem precisar buscar seus dados em outro sistema. O JWT funciona assim — depois do login, o servidor gera um token criptografado, como um passaporte, com informações como userId e email, e o armazena em um cookie no navegador.

Vantagens:

  • É rápido. Não há consulta ao banco; basta descriptografar o token para identificar o usuário.
  • É econômico. Não exige um banco para armazenar sessões, o que é especialmente útil em implantações serverless.
  • É escalável. Mesmo com muitos usuários, não existe uma tabela de sessões crescendo sem parar.

Desvantagens:

  • Não permite forçar o logout de um usuário. Descobriu que uma conta foi invadida e quer invalidar aquele login? Enquanto o token não expirar, não há como revogá-lo — a menos que você mantenha uma lista de bloqueio, o que volta a exigir um banco de dados.
  • Não permite limitar o número de dispositivos conectados. Quer estabelecer “no máximo três dispositivos ao mesmo tempo”? Não é possível.
  • As informações não podem ser atualizadas antes de o token expirar. Se você armazenou a função do usuário no token e ela mudou, o usuário continuará vendo a função antiga até a expiração.

Database Session: o modelo do cartão de hotel

Um hotel entrega um cartão que contém apenas o número do quarto; seus dados completos, como documento e despesas, ficam no sistema do hotel. A cada uso do cartão, o sistema consulta o quarto e verifica sua permissão. Database Session funciona da mesma forma — o cookie guarda apenas um ID de sessão, enquanto os dados reais do usuário ficam no banco.

Vantagens:

  • Permite revogar um login a qualquer momento. O usuário escolheu “sair de todos os dispositivos”? Basta apagar os registros de sessão no banco.
  • Permite limitar o número de dispositivos conectados.
  • Permite atualizar os dados do usuário em tempo real; se uma permissão mudar, a alteração vale já na próxima requisição.

Desvantagens:

  • É mais lento. Cada requisição precisa consultar o banco.
  • Exige o gerenciamento da tabela de sessões, incluindo criação e limpeza de sessões expiradas.
  • Com muitos usuários, a pressão sobre o banco de dados aumenta.

Árvore de decisão (a parte importante)

Você provavelmente quer saber: qual devo usar? Minha sugestão:

Sua aplicação precisa forçar o usuário a entrar novamente?
(Por exemplo, após mudar a senha ou bloquear a conta.)
  ├─ Sim → Use Database Session
  └─ Não → Continue para a próxima pergunta

Você quer dispensar o banco de dados ou fazer uma implantação serverless?
  ├─ Sim → Use JWT
  └─ Não → Continue para a próxima pergunta

Sua aplicação é um projeto pessoal ou MVP que precisa ir ao ar rapidamente?
  ├─ Sim → Use JWT (simples e prático)
  └─ Não → Use Database Session (mais adequado para aplicações empresariais)

No meu próprio projeto, comecei com JWT. Mais tarde, criei um painel de gerenciamento de usuários e precisei encerrar sessões remotamente, então migrei para Database Session. O custo da mudança foi considerável; vale pensar nisso desde o início.

Caso especial: Credentials + Database Session

Se você usa o provedor Credentials e também quer Database Session, encontra um grande obstáculo: a documentação oficial diz que essa combinação não é compatível.

Provedores OAuth, como Google e GitHub, funcionam perfeitamente com Database Session, mas Credentials não. O motivo é que o NextAuth.js considera Credentials flexível demais para criar registros de sessão automaticamente.

Existem soluções, mas elas exigem código manual para criar a sessão no callback signIn. Há várias discussões no GitHub, como esta issue, com implementações que você pode consultar. Sinceramente, se você está começando, recomendo usar JWT ou trocar para um provedor OAuth em vez de aumentar a complexidade.

Configuração completa do provedor Credentials

Chega de teoria; vamos ao código. Vou mostrar três versões, da mais simples à completa, para você escolher conforme a etapa do seu projeto.

Configuração básica: a menor versão funcional

Primeiro, instale a dependência:

npm install next-auth

Depois, crie o arquivo. No Next.js 13+ com App Router, o caminho é app/api/auth/[...nextauth]/route.ts. Se você ainda usa Pages Router, o caminho é pages/api/auth/[...nextauth].js. Aqui usarei App Router.

app/api/auth/[…nextauth]/route.ts:

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"

const handler = NextAuth({
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        email: { label: "邮箱", type: "email" },
        password: { label: "密码", type: "password" }
      },
      async authorize(credentials) {
        // 这里先hardcode一个用户,方便测试
        if (credentials?.email === "[email protected]" &&
            credentials?.password === "123456") {
          return {
            id: "1",
            name: "测试用户",
            email: "[email protected]"
          }
        }
        return null  // 登录失败
      }
    })
  ],
  session: {
    strategy: "jwt"  // 使用JWT session
  },
  pages: {
    signIn: '/login'  // 自定义登录页面(可选)
  }
})

export { handler as GET, handler as POST }

Variáveis de ambiente em .env.local:

NEXTAUTH_SECRET=your-super-secret-key-change-this
NEXTAUTH_URL=http://localhost:3000

Pontos importantes:

  • NEXTAUTH_SECRET: usado para criptografar o token. É obrigatório em produção e também recomendado no desenvolvimento local. Para gerar: openssl rand -base64 32.
  • Função authorize: é o núcleo da validação da identidade. Retorne o objeto do usuário em caso de sucesso ou null em caso de falha.

Essa versão funciona, mas os dados do usuário estão fixos no código e não servem para uma aplicação real. Agora vamos conectar o banco de dados.

Detalhes das principais opções de configuração

Antes do exemplo completo, vale explicar as opções que mais causam confusão.

1. session.strategy: "jwt" ou "database"?

Como vimos, o padrão é "jwt". Se você usa um Adapter para conectar o banco, a estratégia muda automaticamente para "database". Porém, ao combinar o provedor Credentials com JWT, é melhor definir explicitamente strategy: "jwt"; caso contrário, pode ocorrer um erro.

2. callbacks: como adicionar campos personalizados à session?

Por padrão, useSession retorna apenas name, email e image nos dados do usuário. E se você quiser adicionar userId ou role?

Use callbacks:

callbacks: {
  async jwt({ token, user }) {
    // user 只在登录时有值
    if (user) {
      token.userId = user.id  // 把userId加到token里
    }
    return token
  },
  async session({ session, token }) {
    // 把token里的userId放到session里
    session.user.userId = token.userId
    return session
  }
}

Assim, quando o frontend chamar const { data: session } = useSession(), session.user.userId terá um valor.

3. pages: página de login personalizada

O NextAuth.js inclui uma página de login pouco atraente em /api/auth/signin. Para usar sua própria interface, defina pages: { signIn: '/login' }.

Sua página de login deve chamar:

import { signIn } from "next-auth/react"

const handleSubmit = async (e) => {
  e.preventDefault()
  const result = await signIn('credentials', {
    redirect: false,
    email,
    password
  })

  if (result?.error) {
    // 登录失败
  } else {
    // 登录成功,跳转
  }
}

Exemplo completo: cadastro, login e gerenciamento de sessão

Agora veremos uma versão adequada para uso real. Vamos supor que você use Prisma + PostgreSQL.

1. Crie primeiro a tabela de usuários

prisma/schema.prisma:

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  password  String
  name      String?
  createdAt DateTime @default(now())
}

Execute npx prisma migrate dev para gerar a tabela.

2. Endpoint de cadastro

app/api/register/route.ts:

import { NextResponse } from "next/server"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"  // 假设你有个prisma client

export async function POST(req: Request) {
  try {
    const { email, password, name } = await req.json()

    // 检查用户是否已存在
    const existingUser = await prisma.user.findUnique({
      where: { email }
    })

    if (existingUser) {
      return NextResponse.json(
        { error: "邮箱已被注册" },
        { status: 400 }
      )
    }

    // 密码加密(重点!)
    const hashedPassword = await bcrypt.hash(password, 10)

    // 创建用户
    const user = await prisma.user.create({
      data: {
        email,
        password: hashedPassword,
        name
      }
    })

    return NextResponse.json({
      user: {
        id: user.id,
        email: user.email,
        name: user.name
      }
    })
  } catch (error) {
    return NextResponse.json(
      { error: "注册失败" },
      { status: 500 }
    )
  }
}

Importante: a senha precisa ser armazenada com hash. Use bcrypt ou argon2; nunca salve a senha em texto simples no banco.

3. Configuração do NextAuth conectada ao banco de dados

app/api/auth/[...nextauth]/route.ts:

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"

const handler = NextAuth({
  providers: [
    CredentialsProvider({
      credentials: {
        email: { label: "邮箱", type: "email" },
        password: { label: "密码", type: "password" }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          return null
        }

        // 从数据库查用户
        const user = await prisma.user.findUnique({
          where: { email: credentials.email }
        })

        if (!user) {
          return null  // 用户不存在
        }

        // 验证密码
        const isValid = await bcrypt.compare(
          credentials.password,
          user.password
        )

        if (!isValid) {
          return null  // 密码错误
        }

        // 返回用户信息(不要包含密码!)
        return {
          id: user.id,
          email: user.email,
          name: user.name
        }
      }
    })
  ],
  session: {
    strategy: "jwt",
    maxAge: 30 * 24 * 60 * 60  // 30天
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.userId = user.id
      }
      return token
    },
    async session({ session, token }) {
      session.user.userId = token.userId as string
      return session
    }
  },
  pages: {
    signIn: '/login'
  }
})

export { handler as GET, handler as POST }

4. Use a session no frontend

Em um componente de servidor (Server Component):

import { getServerSession } from "next-auth"

export default async function ProfilePage() {
  const session = await getServerSession()

  if (!session) {
    redirect('/login')
  }

  return <div>欢迎, {session.user.name}</div>
}

Em um componente de cliente (Client Component):

'use client'
import { useSession } from "next-auth/react"

export default function Dashboard() {
  const { data: session, status } = useSession()

  if (status === "loading") {
    return <div>加载中...</div>
  }

  if (!session) {
    return <div>请先登录</div>
  }

  return <div>你的用户ID: {session.user.userId}</div>
}

Observação:

  • No servidor, use getServerSession().
  • No cliente, use useSession().
  • O componente de cliente deve estar envolvido por <SessionProvider>, geralmente adicionado ao layout.

Estratégias de gerenciamento de sessão e problemas comuns

Como usar uma sessão JWT corretamente

Se você escolheu JWT, precisa aproveitar bem a estratégia. Veja os pontos principais.

1. Armazene informações extras no JWT, como userId e role

Já mostramos isso no exemplo de callbacks, mas vale reforçar: o callback jwt adiciona dados ao token; o callback session transfere esses dados do token para a session.

Em um projeto real, talvez você também precise armazenar a função do usuário:

callbacks: {
  async jwt({ token, user }) {
    if (user) {
      token.userId = user.id
      token.role = user.role  // 假设数据库有role字段
    }
    return token
  },
  async session({ session, token }) {
    session.user.userId = token.userId as string
    session.user.role = token.role as string
    return session
  }
}

2. Defina o tempo de expiração do token

O padrão é 30 dias, mas você pode alterar:

session: {
  strategy: "jwt",
  maxAge: 7 * 24 * 60 * 60  // 7天
}

Há um detalhe: se o usuário fechar e abrir o navegador, o token continuará lá, a menos que ele faça logout manualmente. Se você quer encerrar a sessão ao fechar o navegador, precisa tratar isso no frontend; o JWT no backend não consegue fazer isso sozinho.

3. A maior limitação do JWT: não é possível revogá-lo ativamente

Essa é a parte mais incômoda do JWT. Imagine que você descubra que a conta de um usuário foi invadida e queira desconectá-lo imediatamente. Não é possível: enquanto o token não expirar, quem estiver com ele continuará tendo acesso.

Alternativas:

  • Defina uma expiração mais curta, como uma hora, trocando parte da experiência do usuário por mais segurança.
  • Mantenha uma lista de tokens bloqueados, mas isso exige um banco de dados e elimina parte da vantagem do JWT.
  • Use Database Session para resolver a questão de vez.

Implementando Database Session com o provedor Credentials

Se você escolheu Database Session desde o início, a configuração é mais simples — desde que use um provedor OAuth, como Google ou GitHub.

Instale um Adapter:

npm install @next-auth/prisma-adapter

Configure:

import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { prisma } from "@/lib/prisma"

const handler = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!
    })
  ]
  // strategy会自动变成"database",不用手动设
})

O banco criará automaticamente as tabelas User, Session, Account e outras.

Porém, se você usa o provedor Credentials, a situação fica mais complicada. A documentação oficial afirma: o Credentials provider não oferece suporte a database session.

Há várias soluções na internet. Em geral, a ideia é criar manualmente o registro de sessão no callback signIn. Não recomendo isso para iniciantes, pois é fácil cometer erros. Se você realmente precisa combinar Credentials + Database Session, consulte estas discussões:

Outra opção é usar Clerk ou Supabase Auth, que oferecem suporte nativo a essa combinação.

Os cinco erros mais comuns

Eu cometi todos estes erros; esta lista pode poupar bastante tempo:

1. Esquecer NEXTAUTH_SECRET e receber um erro em produção

No desenvolvimento local, é possível deixar NEXTAUTH_SECRET sem valor; o NextAuth.js mostra um aviso, mas funciona. Porém, em plataformas como Vercel e Railway, a ausência dessa variável de ambiente provoca diretamente um erro 500.

Gere o valor com openssl rand -base64 32 e adicione-o às variáveis de ambiente da plataforma de implantação.

2. O provedor Credentials não funciona com database session por padrão

Vale repetir: se você configurar um Adapter e também usar Credentials, ocorrerá um erro. Defina explicitamente session: { strategy: "jwt" } ou implemente manualmente a criação da sessão.

3. useSession não funciona em componentes de servidor

No Next.js 13+ com App Router, os componentes são Server Components por padrão. Se você escrever const session = useSession() em um componente de servidor, ocorrerá um erro, porque hooks só podem ser usados no cliente.

No servidor, use getServerSession():

import { getServerSession } from "next-auth"

const session = await getServerSession()

No cliente, adicione a diretiva 'use client' e use useSession().

4. Problemas de cookie entre domínios

No desenvolvimento você usa localhost:3000; em produção, example.com. Como o domínio do cookie é diferente, o estado do login pode se perder.

Solução: confirme que a variável de ambiente NEXTAUTH_URL aponta para o domínio correto em produção. Não deixe localhost fixo no código.

5. Erro JWEDecryptionFailed

Mensagem: JWEDecryptionFailed: decryption operation failed

Causa: você alterou NEXTAUTH_SECRET, mas o navegador ainda guarda o token anterior. O novo secret não consegue descriptografar esse token, então o erro ocorre.

Solução: limpe os cookies do navegador ou faça login novamente.

Extra: proteja rotas com Middleware

Se você quer exigir login em determinadas páginas, pode usar Middleware:

middleware.ts:

export { default } from "next-auth/middleware"

export const config = {
  matcher: ["/dashboard/:path*", "/profile/:path*"]
}

Assim, todas as páginas em /dashboard e /profile verificam a sessão e redirecionam automaticamente para a página de login quando necessário.

Recomendações para projetos reais e próximos passos

Qual solução devo escolher?

Depois de tanta informação, vamos resumir a decisão.

Cenário 1: projeto pessoal, MVP ou blog

  • Recomendação: NextAuth.js + JWT + Credentials
  • Motivo: é gratuito, simples de configurar e dispensa uma tabela de sessões.
  • Desvantagem: se você precisar mais tarde de recursos como “desconectar um usuário”, a migração terá um custo alto.

Cenário 2: aplicação empresarial ou SaaS com orçamento

  • Recomendação: Clerk
  • Motivo: fica pronto em 30 minutos, oferece uma interface bonita e recursos completos de gerenciamento de usuários, economizando de 40 a 80 horas de desenvolvimento.
  • Desvantagem: é pago acima de 10.000 usuários ativos mensais e limita a personalização.
  • Complemento: se você usa o banco de dados do Supabase, Supabase Auth também é uma ótima opção, com cota gratuita de 50.000 MAU.

Cenário 3: aplicação empresarial sem orçamento ou que exige controle total

  • Recomendação: NextAuth.js + Database Session + provedor OAuth, como Google ou GitHub.
  • Motivo: é gratuito, completo e permite encerrar sessões remotamente.
  • Desvantagem: você precisa criar a interface de login e administrar o banco de dados.

Cenário 4: sistema de usuários existente que precisa apenas de uma camada de autenticação

  • Recomendação: NextAuth.js + Credentials + JWT
  • Motivo: é flexível e não interfere na estrutura atual do banco.
  • Atenção: você precisa implementar as medidas de segurança, como hash de senha e proteção contra força bruta.

Minha experiência: no primeiro projeto, usei JWT. Seis meses depois, os requisitos mudaram, foi necessário adicionar gerenciamento de usuários e passei dois dias migrando para Database Session. No segundo, avaliei a necessidade desde o início e escolhi Database Session, economizando bastante trabalho.

Roteiro de aprendizado avançado

Depois de fazer o login com Credentials funcionar, você pode aprender:

1. Provedores OAuth, que são mais simples e devem ser uma prioridade

  • O login com Google ou GitHub é bem mais simples que Credentials.
  • Você não precisa cuidar do hash de senha nem do cadastro; o provedor OAuth faz isso.
  • A experiência também é melhor, com login em um clique.

2. Middleware para proteger rotas

  • Como mostramos antes, uma linha de código pode proteger um diretório inteiro.
  • É muito mais prático do que verificar a sessão individualmente em cada página.

3. Gerenciamento de permissões com várias funções (RBAC)

  • Armazene o campo role na session.
  • Exiba conteúdos e conceda permissões conforme a função.
  • Como etapa avançada, conheça a biblioteca CASL para controle de permissões granular.

4. Verificação de email e redefinição de senha

  • Use o Email Provider do NextAuth.js para enviar links de login por email.
  • Implemente o fluxo de esqueci minha senha, enviando um link de redefinição.

Recursos de referência

Documentação oficial (leitura essencial):

Tutoriais úteis:

Discussões no GitHub para consultar ao encontrar problemas:

Comparações com concorrentes para ajudar na escolha:

Conclusão

Depois de tudo isso, o NextAuth.js se resume a três decisões:

  1. Qual método de login usar? Credentials ou OAuth? Na maioria dos casos, OAuth, como Google ou GitHub, é mais simples.
  2. Como armazenar a sessão? JWT ou Database? Use JWT em projetos pessoais e Database em aplicações empresariais.
  3. Como validar o usuário? Com Credentials, você escreve a lógica; com OAuth, o provedor faz a validação.

Minha sugestão: faça primeiro o menor exemplo funcional deste artigo. Depois que o login estiver funcionando, adicione os recursos aos poucos. Não tente dominar todas as configurações logo no começo; isso só torna o processo desanimador.

O NextAuth.js realmente tem uma curva de aprendizado, mas, depois de dominá-lo, você controla todo o fluxo de autenticação. Se quiser rapidez, Clerk é uma boa escolha; se quiser economizar e aprender, vale investir tempo no NextAuth.js.

Por fim, conte nos comentários qual problema você encontrou. Em que etapa da configuração você travou? Ainda está em dúvida entre JWT e Session?

Leitura recomendada:

  • O próximo artigo está planejado como um “Guia completo de Next.js Middleware”, com proteção de rotas, testes A/B e outros cenários.

  • Se você se interessa por gerenciamento de permissões de usuários, leia meu artigo anterior sobre “práticas de design de permissões RBAC”.

Fluxo completo de configuração de login com Credentials no NextAuth.js

Todas as etapas, da instalação à implementação de login com usuário e senha e ao gerenciamento de sessão

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: Instale e inicialize o NextAuth.js

    Instale a dependência:
    • npm install next-auth
    • Crie app/api/auth/[...nextauth]/route.ts

    Configuração básica:
    • Defina NEXTAUTH_URL (local: http://localhost:3000)
    • Defina NEXTAUTH_SECRET (gere uma string aleatória)
    • Configure o array providers
  2. 2

    Step 2: Configure o Credentials Provider

    Implemente a lógica de validação:
    1. Valide o usuário na função authorize do CredentialsProvider
    2. Busque o usuário no banco de dados (ou use dados fixos para teste)
    3. Verifique a senha (com bcrypt ou outra biblioteca)
    4. Retorne o objeto do usuário (com id, name, email etc.)

    Atenção:
    • A função authorize deve retornar um objeto de usuário ou null
    • O objeto retornado será armazenado na session
    • A verificação da senha deve ocorrer no servidor
  3. 3

    Step 3: Escolha a estratégia de sessão (JWT ou Session)

    Estratégia JWT (padrão):
    • Indicada para aplicações sem estado
    • Os dados da sessão ficam armazenados no token JWT
    • Não exige banco de dados
    • Configuração: session: { strategy: 'jwt' }

    Estratégia Session (exige banco de dados):
    • Indicada para aplicações que precisam revogar logins
    • Os dados da sessão ficam armazenados no banco de dados
    • Exige um Adapter, como Prisma ou MongoDB
    • Configuração: session: { strategy: 'database' }
  4. 4

    Step 4: Configure Callbacks para personalizar o fluxo

    Callbacks comuns:
    • signIn: controla se o login será permitido
    • jwt: personaliza o conteúdo do token JWT (estratégia JWT)
    • session: personaliza o conteúdo da session

    Exemplo:
    callbacks: {
    async jwt({ token, user }) {
    if (user) token.role = user.role
    return token
    },
    async session({ session, token }) {
    session.user.role = token.role
    return session
    }
    }
  5. 5

    Step 5: Crie a página e os componentes de login

    Use a API do NextAuth.js:
    • signIn('credentials', { username, password }): inicia o login
    • signOut(): encerra a sessão
    • useSession(): obtém a sessão atual
    • SessionProvider: envolve a aplicação e fornece o contexto da sessão

    Exemplo:
    const { data: session } = useSession()
    if (session) {
    return <div>Usuário conectado: {session.user.name}</div>
    }
  6. 6

    Step 6: Teste e depure

    Pontos de teste:
    • Verifique se usuário e senha corretos permitem o login
    • Verifique se credenciais incorretas são recusadas
    • Verifique se a sessão persiste
    • Verifique se o logout remove a sessão

    Como depurar:
    • Consulte os erros no console do navegador
    • Confira os logs do servidor
    • Ative o modo de depuração do NextAuth.js
    • Verifique se as variáveis de ambiente estão definidas corretamente

FAQ

Qual é a diferença entre NextAuth.js, Clerk e Supabase Auth?
NextAuth.js:
• Solução auto-hospedada, gratuita e de código aberto
• Exige que você implemente a interface de login e o gerenciamento de usuários
• Oferece controle total

Clerk:
• Oferece interface completa e painel de gerenciamento
• Passa a ser pago acima de 10.000 usuários ativos mensais

Supabase Auth:
• Indicado para projetos que usam o banco de dados do Supabase
• A integração é simples, mas fica vinculada ao banco
Devo escolher JWT ou Session?
JWT é indicado para aplicações sem estado e projetos pessoais. Não exige banco de dados, mas não permite revogar uma sessão específica. Session é melhor para aplicações empresariais e cenários que exigem revogação de login. Requer um Adapter de banco de dados, mas permite controle preciso das sessões.
O login com Credentials é seguro?
Sim, desde que você tome alguns cuidados:
1) Armazene as senhas com hash, usando bcrypt ou outra biblioteca
2) Execute a lógica de validação no servidor
3) Use HTTPS na transmissão
4) Defina requisitos de senha forte
5) Considere adicionar CAPTCHA para evitar ataques de força bruta
Como personalizar a página de login?
Use a opção pages na configuração do NextAuth: pages: { signIn: '/auth/login' }.

Depois, crie a página de login personalizada e chame signIn('credentials', { username, password }) para iniciar o login.

Também é possível personalizar toda a interface e usar apenas a API do NextAuth.js.
Como obter o usuário conectado no momento?
No cliente, use o hook useSession():
const { data: session } = useSession()

No servidor, use getServerSession():
const session = await getServerSession(authOptions)

O objeto session contém os dados de user, como id, name e email.
Como implementar controle de funções e permissões?
Personalize o conteúdo de JWT ou Session nos callbacks e adicione o campo role. Depois, verifique session.user.role nas páginas ou rotas de API e permita o acesso conforme a função. Você também pode usar Middleware para fazer uma verificação global de permissões.
Quais bancos de dados são compatíveis com NextAuth.js?
O NextAuth.js oferece suporte a vários bancos por meio de Adapters: Prisma (PostgreSQL, MySQL, SQLite e outros), MongoDB, TypeORM, Drizzle ORM etc. Ao usar um Adapter, a estratégia muda automaticamente para Session, e os dados da sessão ficam armazenados no banco de dados.

19 min de leitura · Publicado em: 19 dez 2025 · Atualizado em: 4 set 2026

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog