Autenticação e Autorização
Este documento descreve os dois mecanismos de autenticação do sistema: JWT via cookie para usuários da interface web (administradores e clientes) e API Keys para integrações de terceiros e automações programáticas.
1. Visão Geral
O sistema opera com dois mecanismos independentes e complementares:
| Mecanismo | Destino | Transporte | Expiração |
|---|---|---|---|
| JWT HS256 | Usuários web (admin/cliente) | Cookie token (HttpOnly) | 7 dias |
| API Key | Integrações, scripts, ERPs | Header Authorization: Bearer lvk_live_... | Sem expiração (revogação manual) |
Ambos são verificados no Edge Runtime pelo middleware.ts antes de qualquer Route Handler ser atingido, garantindo que nenhuma rota protegida seja acessível sem credenciais válidas.
2. JWT — Fluxo Completo
2.1 Algoritmo e Biblioteca
O token é assinado com HMAC-SHA256 (HS256) usando a biblioteca jose, que opera nativamente no Edge Runtime (sem dependência de módulos Node.js).
A chave de assinatura é lida de JWT_SECRET (variável de ambiente obrigatória). Recomenda-se que tenha no mínimo 256 bits de entropia:
openssl rand -hex 32
2.2 Diagrama do Fluxo
Usuário Next.js (Route Handler) Middleware (Edge)
| | |
|-- POST /api/auth/login ---->| |
| { email, password } | |
| |-- verifica credenciais |
| |-- signToken(userId, email, role)
| |-- Set-Cookie: token=<JWT> |
|<-- 200 OK + Set-Cookie -----| |
| | |
|-- GET /admin/dashboard ---->|-----> middleware.ts |
| Cookie: token=<JWT> | verifyToken(token) |
| | payload { userId, ... } |
| |<----- request headers |
| |-- Route Handler executa |
|<-- 200 HTML ----------------| |
2.3 Payload do Token
// Campos incluídos no payload JWT
{
userId: string; // ID interno do usuário (UUID)
email: string; // E-mail do usuário
role: "admin" | "customer"; // Papel para controle de acesso
iat: number; // Issued at (Unix timestamp)
exp: number; // Expiration (iat + 7 dias)
}
2.4 Expiração e Renovação
Os tokens expiram 7 dias após a emissão. O sistema não implementa refresh token automático — ao expirar, o usuário é redirecionado para /login. A renovação ocorre a cada novo login bem-sucedido, que emite um token com nova janela de 7 dias.
Decisão de design: a ausência de refresh token simplifica a implementação e é aceitável para o padrão de uso do admin (sessões de trabalho curtas). Caso necessário no futuro, adicionar um endpoint
/api/auth/refreshque verifica o token expirado e emite um novo, desde que a expiração seja recente (ex.: menos de 1 dia).
3. Cookie token
3.1 Atributos
| Atributo | Valor | Finalidade |
|---|---|---|
HttpOnly | true | Impede acesso via document.cookie (XSS mitigation) |
Secure | true em produção, false em dev | Transmissão apenas via HTTPS |
SameSite | Lax | Proteção contra CSRF; permite navegação entre páginas normais |
Path | / | Válido para todas as rotas |
MaxAge | 604800 (7 dias em segundos) | Alinhado com a expiração do JWT |
3.2 Leitura do Cookie por Contexto
No Middleware (Edge Runtime):
// middleware.ts — via request.cookies
const token = request.cookies.get("token")?.value;
Em Route Handlers (Node.js Runtime):
// app/api/.../route.ts — via next/headers
import { cookies } from "next/headers";
const cookieStore = cookies();
const token = cookieStore.get("token")?.value;
Em Server Components:
// app/.../page.tsx
import { getSession } from "@/lib/auth";
const session = await getSession(); // retorna null se não autenticado
4. Helpers de Autenticação (lib/auth.ts)
| Função | Assinatura | Quando Usar |
|---|---|---|
signToken | (payload: JwtPayload) => Promise<string> | Após login bem-sucedido para gerar o JWT |
verifyToken | (token: string) => Promise<JwtPayload | null> | No middleware e em handlers que precisam do payload |
getSession | () => Promise<JwtPayload | null> | Em Server Components e Route Handlers para obter o usuário atual |
requireAuth | () => Promise<JwtPayload> | Em Route Handlers que exigem usuário autenticado (lança 401 se não autenticado) |
requireAdmin | () => Promise<JwtPayload> | Em Route Handlers exclusivos do admin (lança 403 se role !== "admin") |
Exemplo de Uso
// Em um Route Handler protegido
import { requireAdmin } from "@/lib/auth";
export async function GET() {
const session = await requireAdmin(); // lança Response 401/403 se inválido
// session.userId, session.email, session.role disponíveis
}
5. Middleware (middleware.ts)
O middleware executa no Edge Runtime e intercepta requisições antes de chegarem aos Route Handlers ou às páginas.
5.1 Lógica de Roteamento
5.2 Headers Injetados
Após verificação bem-sucedida, o middleware injeta headers para uso nos Route Handlers:
x-user-id: <userId>
x-user-role: admin
x-user-email: <email>
5.3 Suporte a Subdomínio Admin
O middleware verifica o header host e, se o hostname começar com admin., reescreve a URL internamente para o prefixo /admin, permitindo que admin.loja.com/pedidos seja tratado como /admin/pedidos.
6. API Keys
6.1 Formato
lvk_live_<base64url>
- Prefixo
lvk_live_identifica o ambiente e o sistema (útil para scanners de secret leakage). - A parte
<base64url>é gerada comcrypto.getRandomValues— 32 bytes aleatórios codificados em Base64 URL-safe.
6.2 Geração
import { generateApiKey } from "@/lib/api-auth";
const { key, hash } = await generateApiKey();
// key → "lvk_live_abc123..." (exibido UMA vez ao usuário)
// hash → SHA-256 do key (armazenado no banco)
Regra de ouro: a chave em texto plano nunca é armazenada no banco. Apenas o hash SHA-256 é persistido no model
ApiKey. Ao ser gerada, a chave deve ser exibida uma única vez ao usuário.
6.3 Verificação
// lib/api-auth.ts
export async function verifyApiKey(
req: Request,
scope: ApiKeyScope
): Promise<ApiKeyPayload | null>
O fluxo de verificação:
- Extrai o header
Authorization: Bearer <key>. - Calcula
SHA-256(<key>). - Busca no banco pelo hash.
- Verifica se o escopo solicitado está na lista de escopos da chave.
- Atualiza o campo
lastUsedAtda chave. - Retorna o payload ou
nullse inválido.
6.4 Escopos Disponíveis
| Escopo | Permissão |
|---|---|
products:read | Listar e visualizar produtos |
products:write | Criar, atualizar e excluir produtos |
stock:write | Atualizar estoque de produtos |
orders:read | Listar e visualizar pedidos |
orders:write | Criar e atualizar pedidos |
6.5 Model no Banco de Dados
model ApiKey {
id String @id @default(cuid())
name String // descrição da integração
keyHash String @unique // SHA-256 da chave
scopes String[] // ["products:read", "orders:read"]
lastUsedAt DateTime?
createdAt DateTime @default(now())
revokedAt DateTime? // null = ativa
}
7. Rate Limiting
7.1 Algoritmo
O rate limiting do endpoint /api/auth/login utiliza sliding window em memória (sem dependência de Redis). Isso significa que o estado é por processo — em ambientes multi-instância, o limite é por instância, não global.
7.2 Configuração Atual
| Parâmetro | Valor |
|---|---|
| Endpoint protegido | POST /api/auth/login |
| Chave de identificação | login:{ip} |
| Janela | 15 minutos |
| Máximo de tentativas | 10 requisições |
| Header de resposta | Retry-After: <segundos> |
7.3 Comportamento
1ª–10ª tentativa: 200 (login bem-sucedido) ou 401 (credenciais inválidas)
11ª+ tentativa: 429 Too Many Requests
Retry-After: <segundos até a janela expirar>
{ error: "Muitas tentativas. Tente novamente em X minutos." }
8. Tabela de Permissões por Rota
| Rota | Acesso Permitido | Verificação |
|---|---|---|
GET / e rotas da loja | Público | Nenhuma |
POST /api/auth/login | Público (com rate limit) | Rate limit por IP |
GET /api/products | Público ou products:read | Opcional (API Key) |
POST /api/products | products:write | API Key obrigatória |
GET /api/orders | orders:read ou admin | JWT cookie ou API Key |
/admin/* | role=admin | JWT cookie (middleware) |
/api/admin/* | role=admin | JWT cookie (middleware) |
9. Boas Práticas de Segurança
9.1 JWT_SECRET
- Gerar com entropia adequada:
openssl rand -hex 32 - Nunca versionar no repositório (deve estar em
.enve.gitignore) - Rotacionar o secret invalida todos os tokens ativos — planejar janela de manutenção
- Em produção, definir via painel de variáveis de ambiente do servidor (não via arquivo
.env)
9.2 API Keys
- Exibir a chave em texto plano apenas no momento da criação
- Implementar mecanismo de revogação (setar
revokedAt) sem excluir o registro (para auditoria) - Monitorar
lastUsedAtpara identificar chaves inativas e revogar - Evitar incluir a chave em logs, URLs ou respostas de erro
9.3 Logging
// ❌ NUNCA logar tokens ou chaves
console.log("Token recebido:", token);
// ✅ Logar apenas metadados não-sensíveis
console.log("Autenticação bem-sucedida para userId:", session.userId);
9.4 Checklist de Segurança
-
JWT_SECRETtem 32+ bytes aleatórios e não está no repositório - Cookie
tokenestá comHttpOnly=trueeSecure=trueem produção - API Keys são armazenadas apenas como hash no banco
- Rate limiting está ativo em
/api/auth/login - Middleware protege todas as rotas
/admin/*e/api/admin/* - Não há logs de tokens, senhas ou chaves em produção