Pular para o conteúdo principal

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:

MecanismoDestinoTransporteExpiração
JWT HS256Usuários web (admin/cliente)Cookie token (HttpOnly)7 dias
API KeyIntegrações, scripts, ERPsHeader 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/refresh que verifica o token expirado e emite um novo, desde que a expiração seja recente (ex.: menos de 1 dia).


3.1 Atributos

AtributoValorFinalidade
HttpOnlytrueImpede acesso via document.cookie (XSS mitigation)
Securetrue em produção, false em devTransmissão apenas via HTTPS
SameSiteLaxProteção contra CSRF; permite navegação entre páginas normais
Path/Válido para todas as rotas
MaxAge604800 (7 dias em segundos)Alinhado com a expiração do JWT

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çãoAssinaturaQuando 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 com crypto.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:

  1. Extrai o header Authorization: Bearer <key>.
  2. Calcula SHA-256(<key>).
  3. Busca no banco pelo hash.
  4. Verifica se o escopo solicitado está na lista de escopos da chave.
  5. Atualiza o campo lastUsedAt da chave.
  6. Retorna o payload ou null se inválido.

6.4 Escopos Disponíveis

EscopoPermissão
products:readListar e visualizar produtos
products:writeCriar, atualizar e excluir produtos
stock:writeAtualizar estoque de produtos
orders:readListar e visualizar pedidos
orders:writeCriar 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âmetroValor
Endpoint protegidoPOST /api/auth/login
Chave de identificaçãologin:{ip}
Janela15 minutos
Máximo de tentativas10 requisições
Header de respostaRetry-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

RotaAcesso PermitidoVerificação
GET / e rotas da lojaPúblicoNenhuma
POST /api/auth/loginPúblico (com rate limit)Rate limit por IP
GET /api/productsPúblico ou products:readOpcional (API Key)
POST /api/productsproducts:writeAPI Key obrigatória
GET /api/ordersorders:read ou adminJWT cookie ou API Key
/admin/*role=adminJWT cookie (middleware)
/api/admin/*role=adminJWT 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 .env e .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 lastUsedAt para 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_SECRET tem 32+ bytes aleatórios e não está no repositório
  • Cookie token está com HttpOnly=true e Secure=true em 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