Next.js: Autenticação & Autorização
Última atualização: 2026-08-26
Adicionar autenticação a uma aplicação full-stack é como instalar um sistema de controle de acesso em um prédio de escritórios — você precisa de controle preciso sobre quem pode entrar e quais andares podem acessar.
1. O Que Você Vai Aprender
- Integração do Auth.js v5 (NextAuth) com Múltiplos Provedores: Credentials / GitHub / Google
- Seleção e Configuração de Estratégias de Sessão JWT e Database
- Proteção de Rotas com Middleware e
matcherExcluindo Caminhos Comuns - Integração de Autenticação de Terceiros com Clerk (
<SignIn />/<SignUp />/ helperauth()) - RBAC: Controle de Acesso Baseado em Funções (admin / editor / viewer)
getToken()para Autenticação em Rotas de API Protegidas
2. Uma História Real de um Desenvolvedor Full-Stack
(1) Ponto de Dor: Não percebemos que faltava autenticação até pouco antes do lançamento
Alice trabalha em uma startup SaaS de 15 pessoas. Ela desenvolveu o "TaskFlow", uma plataforma de colaboração em equipe completa, usando Next.js 16. No entanto, durante uma auditoria de segurança antes do lançamento, Bob, o líder técnico, apontou:
"Sua página
/dashboardestá acessível para qualquer pessoa; o endpoint da API não tem autenticação por token; os dados do usuário estão expostos em texto puro."
O relatório de auditoria lista os seguintes problemas:
| Problema | Escopo | Nível de Risco |
|---|---|---|
| Sem Página de Login | Todas as Rotas | 🔴 Alto |
| Rota API: Sem Autenticação | /api/projects/* |
🔴 Alto |
| Sem distinção de funções | Todos os usuários podem ver o painel admin | 🟡 Médio |
| Sessão nunca expira | Login único e permanece logado para sempre | 🔴 Alto |
(2) A Solução com Auth.js + Clerk
Implemente o fluxo de autenticação padrão usando Auth.js v5, com Clerk como alternativa de configuração zero.
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Google from 'next-auth/providers/google'
import Credentials from 'next-auth/providers/credentials'
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
GitHub,
Google,
Credentials({
credentials: { email: {}, password: {} },
async authorize(credentials) {
const user = { id: '1', name: 'Alice', email: 'alice@taskflow.io', role: 'admin' }
return credentials.email === 'alice@taskflow.io' && credentials.password === 'pass123' ? user : null
}
})
],
callbacks: { session: ({ session, token }) => ({ ...session, user: { ...session.user, role: token.role } }) }
})
(3) Ganhos
| Dimensão | Antes da Implementação | Depois da Implementação |
|---|---|---|
Acessar /dashboard sem login |
Acessível | Redirecionado para página de login |
| Segurança do Endpoint API | Sem Validação | Validação JWT com getToken() |
| Controle de Funções | Nenhum | Três níveis: admin, editor, viewer |
| Expiração de Sessão | Permanente | Expira automaticamente após 30 dias |
| Tempo de Integração | — | Auth.js: 2h / Clerk: 30min |
3. Integração de Provedores com Auth.js v5 (NextAuth)
(1) Arquitetura de Provedores
A camada de abstração de Provider no Auth.js v5 permite integrar múltiplas fontes de autenticação através de uma interface unificada:
graph TB
A[Requisição de Autenticação] --> B{Route Handler do NextAuth}
B --> C[Credentials<br/>Email+Senha]
B --> D[OAuth<br/>GitHub / Google]
B --> E[Outro Provider<br/>Auth0 / Azure AD]
C --> F[JWT Callback<br/>token + session]
D --> F
E --> F
F --> G[Sessão Retornada ao cliente]
G --> H[Verificação no Middleware]
H --> I[Página Protegida]
H --> J[Página Pública]
style B fill:#cce5ff
style F fill:#d4edda
| Tipo de Provider | Complexidade de Implementação | Experiência do Usuário | Cenários Aplicáveis |
|---|---|---|---|
| Credentials | Média (requer página de login personalizada) | Formulário padrão | Sistema de contas próprio |
| GitHub OAuth | Baixa | Login com um clique | Ferramentas para desenvolvedores |
| Google OAuth | Baixa | Login com um clique | Para usuários em geral |
| OIDC / SAML | Alta | SSO Empresarial | Intranet corporativa |
(2) Instalação e Inicialização
npm install next-auth@beta
npx auth secret # Gerar AUTH_SECRET
// auth.ts — Configuração Centralizada de Autenticação
import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github'
import Google from 'next-auth/providers/google'
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
GitHub({ clientId: process.env.GITHUB_ID!, clientSecret: process.env.GITHUB_SECRET! }),
Google({ clientId: process.env.GOOGLE_ID!, clientSecret: process.env.GOOGLE_SECRET! })
]
})
▶ Exemplo: Configuração Completa de um Provider Credentials
Saída:
TypeScript code executed successfully.
// auth.ts — Credentials + OAuth Misto
import NextAuth from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import GitHub from 'next-auth/providers/github'
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Credentials({
name: 'credentials',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Senha', type: 'password' }
},
async authorize(credentials) {
const { email, password } = credentials as { email: string; password: string }
// Projeto Real: Consultar Banco de Dados
const user = { id: '1', name: 'Alice', email, role: 'admin' }
if (email === 'admin@taskflow.io' && password === 'admin123') return user
return null
}
}),
GitHub
],
callbacks: {
async jwt({ token, user }) {
if (user) token.role = (user as any).role
return token
},
async session({ session, token }) {
session.user.role = token.role as string
return session
}
}
})
Saída:
TypeScript configuration loaded successfully.
authorize retornar null, significa que o login falhou, e o Auth.js retorna automaticamente uma resposta 401.
4. Gerenciamento de Sessão: JWT vs Database
(1) Comparação das Duas Estratégias
O Auth.js v5 suporta duas estratégias de armazenamento de sessão:
| Dimensão | JWT (padrão) | Database |
|---|---|---|
| Local de Armazenamento | JWT criptografado em um cookie | Tabela Session no banco de dados |
| Sobrecarga de Consulta | Zero (sem consultas ao BD) | Uma consulta ao BD por requisição |
| Expiração Imediata | Depende do Tempo de Expiração do JWT | Pode Ser Revogada Imediatamente |
| Escalabilidade | Sem necessidade de banco de dados | Requer um ORM como Prisma |
| Escala Adequada | Aplicações Pequenas e Médias | Grandes Aplicações Empresariais |
graph LR
subgraph Padrão JWT
A1[Login] --> B1[Gerar JWT<br/>com user + role]
B1 --> C1[Gravar Cookie]
C1 --> D1[Requisição → middleware<br/>Descriptografar JWT → Verificação]
end
subgraph Padrão Database
A2[Login] --> B2[Criar Session<br/>Gravar no BD]
B2 --> C2[Session ID → Cookie]
C2 --> D2[Requisição → middleware<br/>Buscar no BD → Verificação]
end
style A1 fill:#d4edda
style A2 fill:#cce5ff
(2) Configuração de Sessão com Database
// auth.ts — Database Session + Prisma
import NextAuth from 'next-auth'
import { PrismaAdapter } from '@auth/prisma-adapter'
import { prisma } from '@/lib/prisma'
export const { handlers, signIn, signOut, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
session: { strategy: 'database' },
providers: [GitHub, Google]
})
▶ Exemplo: Recuperando uma Sessão e Exibindo Informações do Usuário
Saída:
TypeScript module executes successfully.
// app/dashboard/page.tsx — Recuperação de Sessão no Servidor
import { auth } from '@/auth'
export default async function DashboardPage() {
const session = await auth()
if (!session?.user) return <p>Por favor, faça login primeiro</p>
return (
<div>
<h1>Bem-vindo de volta, {session.user.name}</h1>
<p>Email: {session.user.email}</p>
<p>Função: {session.user.role}</p>
<img src={session.user.image!} alt="avatar" width={48} height={48} />
</div>
)
}
<h1>Bem-vindo de volta, Alice</h1>
<p>Email: alice@taskflow.io</p>
<p>Função: admin</p>
Saída:
<h1>Welcome back, Alice</h1>
<p>Email: alice@taskflow.io</p>
<p>Role: admin</p>
5. Proteção de Rotas com Middleware
(1) Modo de Configuração do Matcher
middleware.ts intercepta a requisição antes que ela chegue à página para verificar a validade da sessão:
graph TB
A[Usuário Requisita /dashboard/*] --> B{middleware.ts}
B -->|Sessão Válida| C[Liberado → page.tsx]
B -->|Sem Sessão| D[Redirecionar /login]
D --> E{Caminhos Públicos?}
E -->|/api/auth/*| F[Liberado]
E -->|/_next/*| F
E -->|/favicon.ico| F
style B fill:#fff3cd
style C fill:#d4edda
style D fill:#f8d7da
| Padrão Matcher | Caminho Correspondente | Descrição |
|---|---|---|
/dashboard/:path* |
/dashboard/* |
Proteger o Dashboard |
/api/projects/:path* |
/api/projects/* |
Proteger API |
| `/((?!auth | _next | favicon).*)` |
(2) Implementação Completa do Middleware
// middleware.ts
import { auth } from '@/auth'
import { NextResponse } from 'next/server'
export default auth((req) => {
const { pathname } = req.nextUrl
const isLoggedIn = !!req.auth
const isPublicPath = pathname.startsWith('/login') ||
pathname.startsWith('/register') ||
pathname.startsWith('/api/auth')
if (!isLoggedIn && !isPublicPath) {
return NextResponse.redirect(new URL('/login', req.url))
}
// RBAC: Impedir que não-admin acesse /admin
if (pathname.startsWith('/admin') && req.auth?.user?.role !== 'admin') {
return NextResponse.redirect(new URL('/dashboard', req.url))
}
return NextResponse.next()
})
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|images).*)']
}
▶ Exemplo: Adicionando Proteção de Autenticação a uma Rota de API
Saída:
Middleware intercepts requests and redirects based on conditions.
// app/api/projects/route.ts — API Protegida
import { getToken } from 'next-auth/jwt'
import { NextRequest, NextResponse } from 'next/server'
export async function GET(req: NextRequest) {
const token = await getToken({ req })
if (!token) {
return NextResponse.json({ error: 'Não está logado' }, { status: 401 })
}
// token.role vem do callback JWT
if (token.role !== 'admin' && token.role !== 'editor') {
return NextResponse.json({ error: 'Permissões insuficientes' }, { status: 403 })
}
return NextResponse.json({ projects: [{ id: 1, name: 'TaskFlow' }] })
}
Saída:
GET app/api/projects/route.ts → Verifica token de autenticação, retorna dados protegidos como JSON.
getToken() requer que a variável de ambiente AUTH_SECRET esteja configurada; caso contrário, retorna null.
6. Autenticação com Clerk (Terceiros)
(1) Comparação de Funcionalidades do Clerk
| Funcionalidade | Auth.js | Clerk |
|---|---|---|
| Complexidade de Instalação | Média (requer configuração de Provider) | Baixa (npm + variáveis de ambiente) |
| Componentes de UI | Página de Login Personalizada | <SignIn /> / <SignUp /> Prontos para Uso |
| Autenticação multifator | Requer integração manual | Suporte integrado |
| Limites do Plano Gratuito | Nenhum | 5.000 MAU (Grátis) |
| UI Personalizada | Liberdade total | Muitas restrições |
(2) Passos para Integrar o Clerk
npm install @clerk/nextjs
# No .env.local, adicione:
# NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=...
# CLERK_SECRET_KEY=...
// app/layout.tsx — ClerkProvider Envolvendo o Layout Raiz
import { ClerkProvider } from '@clerk/nextjs'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<ClerkProvider>
<html lang="pt">
<body>{children}</body>
</html>
</ClerkProvider>
)
}
▶ Exemplo: Página de Login com Clerk + Página Protegida
Saída:
Renders the RootLayout component UI.
// app/page.tsx — Página de Login
import { SignIn, SignedIn, SignedOut, UserButton } from '@clerk/nextjs'
export default function HomePage() {
return (
<div>
<SignedOut>
<SignIn routing="hash" />
</SignedOut>
<SignedIn>
<div>
<UserButton afterSignOutUrl="/" />
<h1>Bem-vindo ao TaskFlow</h1>
</div>
</SignedIn>
</div>
)
}
Saída:
Renders: Bem-vindo ao TaskFlow
Visible text: Bem-vindo ao TaskFlow
// app/dashboard/page.tsx — Proteção com Clerk auth()
import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'
export default async function DashboardPage() {
const { userId } = auth()
if (!userId) redirect('/sign-in')
return <div>Dashboard — UserID: {userId}</div>
}
auth() do Clerk é uma função do lado do servidor; você não precisa de 'use client' ao usá-lo em um Server Component.
(3) Middleware do Clerk
// middleware.ts
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = {
matcher: ['/((?!_next|sign-in|sign-up|favicon.ico).*)']
}
7. RBAC — Controle de Acesso Baseado em Funções
(1) Design do Modelo de Funções
graph TB
A[Usuário] --> B{Função}
B --> C[admin<br/>Acesso Total]
B --> D[editor<br/>Leitura e Escrita de Projetos]
B --> E[viewer<br/>Somente Leitura]
C --> F[Criar/Excluir Itens]
C --> G[Gerenciar Equipe]
C --> H[Alterar Configurações]
D --> I[Editar Tarefas]
D --> J[Adicionar Comentários]
E --> K[Ver Dashboard]
E --> L[Ler Documentos]
style C fill:#d4edda
style D fill:#cce5ff
style E fill:#f8d7da
| Função | Nível de Permissão | Páginas Acessíveis |
|---|---|---|
| admin | 100 | Todas (incluindo /admin) |
| editor | 50 | /dashboard, /projects (editável) |
| viewer | 20 | /dashboard (somente leitura) |
▶ Exemplo: Componente de Verificação de Permissão RBAC
Saída:
Diagram: Usuário; admin Acesso Total; editor Leitura e Escrita de Projetos; viewer Somente Leitura; Criar/Excluir Itens; Gerenciar Equipe.
// components/PermissionGuard.tsx
import { auth } from '@/auth'
import { redirect } from 'next/navigation'
type Role = 'admin' | 'editor' | 'viewer'
const roleHierarchy: Record<Role, number> = { admin: 100, editor: 50, viewer: 20 }
export async function PermissionGuard({
children,
minRole
}: {
children: React.ReactNode
minRole: Role
}) {
const session = await auth()
const userRole = (session?.user?.role as Role) || 'viewer'
if (roleHierarchy[userRole] < roleHierarchy[minRole]) {
redirect('/dashboard')
}
return <>{children}</>
}
Saída:
Renders the PermissionGuard component UI.
// app/admin/page.tsx — Uso do PermissionGuard
import { PermissionGuard } from '@/components/PermissionGuard'
export default function AdminPage() {
return (
<PermissionGuard minRole="admin">
<h1>Console de Gerenciamento</h1>
<p>Esta página só pode ser vista por admin.</p>
</PermissionGuard>
)
}
8. Exemplo Completo: Implementação Abrangente de Autenticação Multi-Provedor + RBAC
// app/dashboard/layout.tsx — Layout Protegido + RBAC
import { auth } from '@/auth'
import { redirect } from 'next/navigation'
import { PermissionGuard } from '@/components/PermissionGuard'
export default async function DashboardLayout({
children,
analytics,
team
}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
const session = await auth()
if (!session) redirect('/login')
const user = session.user!
return (
<div>
<header>
<h1>TaskFlow</h1>
<p>Bem-vindo, {user.name} ({user.role})</p>
<nav>
<a href="/dashboard">Visão Geral</a>
{user.role === 'admin' && <a href="/admin">Gerenciamento</a>}
<a href="/api/auth/signout">Sair</a>
</nav>
</header>
<div style={{ display: 'flex', gap: '2rem' }}>
<main>{children}</main>
<aside>
{analytics}
<PermissionGuard minRole="editor">{team}</PermissionGuard>
</aside>
</div>
</div>
)
}
// app/api/projects/[id]/route.ts — Proteção Completa de API
import { getToken } from 'next-auth/jwt'
import { NextRequest, NextResponse } from 'next/server'
const roles = { admin: 100, editor: 50, viewer: 20 }
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const token = await getToken({ req })
const { id } = await params
if (!token) return NextResponse.json({ error: 'não autorizado' }, { status: 401 })
if (roles[token.role as keyof typeof roles] < 50) {
return NextResponse.json({ error: 'proibido' }, { status: 403 })
}
// Excluir o Item Realmente
return NextResponse.json({ id, deleted: true })
}
{ "error": "não autorizado" }
{ "error": "proibido" }
❓ Perguntas Frequentes
P: Qual é a diferença entre Auth.js v4 (NextAuth) e v5? R: A v5 usa o Route Handler do App Router (
app/api/auth/[...nextauth]/route.ts), suporta recuperar a sessão diretamente em Server Components viaauth(), e não requer maisgetSession()ouSessionProvider.
P: Qual devo escolher: sessões JWT ou sessões de banco de dados? R: Para aplicações pequenas ou que não exigem revogação imediata de sessão, escolha JWT (zero consultas ao BD). Para aplicações empresariais que exigem logout imediato do usuário ou gerenciamento de múltiplos dispositivos, escolha sessões de banco de dados. No modo JWT, a revogação de sessão deve esperar até que o JWT expire (30 dias por padrão).
P: Quando devo escolher Clerk e quando devo escolher Auth.js? R: Se você precisa lançar rapidamente e não quer construir sua própria UI → Clerk (integração em 30 minutos). Se você precisa de uma UI totalmente personalizável e quer construir seu próprio sistema de contas → Auth.js. O plano gratuito do Clerk é limitado a 5.000 MAU; acima disso, custa €25/mês.
P: Qual é a diferença entre
auth()egetToken()no Middleware? R:auth()é uma função wrapper do Auth.js v5 que retorna um objeto Session completo.getToken()é denext-auth/jwte analisa apenas tokens JWT, oferecendo melhor desempenho. Recomendamosauth()por sua simplicidade no middleware; para rotas de API, recomendamosgetToken()por sua leveza.
P: Como protejo páginas exportadas estaticamente? R: Sites estáticos construídos com
output: 'export'não podem usar middleware (que requer o Node.js runtime). Nesse caso, você deve usar o hookuseSession()em um componente cliente para verificar o status de login, ou usar a renderização condicional<SignedIn>/<SignedOut>do Clerk.
📖 Resumo
- Auth.js v5 usa o padrão Route Handler;
auth()pode recuperar a sessão diretamente dentro de um componente servidor - O Provider suporta múltiplas fontes como Credentials, GitHub e Google, e permite injetar dados personalizados via mecanismo de callback
- Sessões JWT com zero consultas ao banco de dados são adequadas para aplicações pequenas, enquanto sessões de banco de dados com suporte a revogação instantânea são adequadas para empresas
- Middleware usa a configuração
matcherpara proteger caminhos e exclui recursos públicos através de correspondência reversa - Clerk fornece componentes de UI prontos para uso, ideal para prototipagem rápida, com um plano gratuito para até 5.000 MAU
- RBAC implementa controle de permissão através de comparações de valores de função e é usado declarativamente em conjunto com o componente PermissionGuard
📝 Exercícios
-
Exercício Básico (⭐): Crie um novo projeto Next.js, integre o Provider GitHub do Auth.js v5 e exiba o avatar e o endereço de email do usuário na página
/dashboard. -
Exercício Avançado (⭐⭐): Adicione lógica RBAC ao middleware:
adminpode acessar/admin/*, eeditorpode acessar a interface de edição/projects/*; retorne uma página 403 se qualquer outra função tentar acessar esses recursos. -
Desafio (⭐⭐⭐): Combinando Auth.js (Provider Credentials) e Prisma: O usuário insere email e senha na página de login →
authorizeconsulta o banco de dados para validar → GravaroleeorgIdno JWT → Middleware redireciona para o workspace da organização correspondente com base noorgId.