Arquitetura de Autenticação
Este documento descreve a arquitetura de autenticação e autorização do Neemias, abrangendo o fluxo completo de login, tokens, sessões e proteção de rotas.
Visão geral
A autenticação é baseada em tokens de acesso JWT de curta duração combinados com tokens de atualização (refresh) de longa duração com rotação. O backend (Cloudflare Workers) gerencia a criação, validação e revogação de sessões. O frontend (React SPA) consome os tokens e mantém a sessão local em IndexedDB.
Tokens e durações
| Token | Algoritmo | TTL | Armazenamento |
|---|---|---|---|
| Access Token | JWT HS256 (jose) | 15 minutos | Memória + IndexedDB (frontend) |
| Refresh Token | Opaco (hash SHA-256 armazenado) | 7 dias | auth_refresh_sessions (backend), IndexedDB (frontend) |
| CSRF Token | Opaco | Vinculado à sessão | IndexedDB (frontend), enviado como X-CSRF-Token |
Fluxo de autenticação
1. Login
POST /api/v1/auth/login { email, password }- Frontend envia
emailepasswordpara o backend.- Se o usuário digitar um identificador curto (
admin,chamador,relatorios,cadastro), o frontend normaliza para o e-mail correspondente (admin@neemias.local, etc.).
- Se o usuário digitar um identificador curto (
- Backend valida a senha usando PBKDF2 (100.000 iterações, SHA-256).
- Formato do hash:
pbkdf2:<hexSalt>:<hexHash>.
- Formato do hash:
- Backend cria uma sessão de autenticação:
- Gera refresh token (opaco).
- Armazena hash SHA-256 do refresh token em
auth_refresh_sessions. - Gera CSRF token vinculado à sessão.
- Assina access token JWT HS256 com payload:
userId,role,sessionId,csrfToken.
- Backend retorna:
accessTokenaccessTokenExpiresAtrefreshTokencsrfTokenuser(payload comuserId,displayName,role,status)
- Frontend persiste os metadados da sessão em IndexedDB (
sessionstable) e os dados do usuário emusers.
2. Requisições autenticadas
Toda requisição autenticada deve incluir:
| Header | Obrigatório | Descrição |
|---|---|---|
Authorization: Bearer <accessToken> | Sim | Token JWT de acesso |
X-Device-Id | Sim | Identificador do dispositivo (obrigatório em todas as requisições) |
X-CSRF-Token | Sim (cookie mode) | Token CSRF para rotas de mutação quando AUTH_SESSION_MODE=cookie |
Idempotency-Key | Sim (mutações) | Chave de idempotência para POST/PATCH |
3. Refresh (rotação de tokens)
POST /api/v1/auth/refresh { refreshToken }- Frontend detecta access token expirado (ou próximo da expiração) e chama o endpoint de refresh.
- Backend valida o hash do refresh token em
auth_refresh_sessions. - Backend aplica rotação de refresh token:
- Revoga o refresh token anterior (
revoked_at,rotated_at). - Cria novo par access/refresh.
- Registra
replaced_byapontando para a nova sessão.
- Revoga o refresh token anterior (
- Frontend substitui os tokens locais pelos novos.
Detecção de reuso malicioso: Se um refresh token já revogado for reutilizado, o backend detecta e invalida toda a cadeia de sessões do usuário.
4. Revogação
POST /api/v1/auth/revoke { refreshToken }Revoga explicitamente uma sessão, marcando revoked_at em auth_refresh_sessions.
5. Validação de sessão
POST /api/v1/session/validateVerifica se a sessão atual está ativa e retorna o estado. Usado pelo frontend para validação periódica.
Modo de desenvolvimento (Dev Mode)
Quando AUTH_MODE=dev está configurado no backend:
- Tokens fixos pré-configurados em variáveis de ambiente são aceitos.
- Não é necessário JWT real.
- Facilita o desenvolvimento local e testes.
Importante: Em produção, AUTH_MODE=jwt é obrigatório (ver Production Readiness Gates).
Hashing de senha
- Algoritmo: PBKDF2 (Password-Based Key Derivation Function 2).
- Iterações: 100.000.
- Hash: SHA-256.
- Formato de armazenamento:
pbkdf2:<hexSalt>:<hexHash>. - Histórico: O backend legado usava scrypt, que se mostrou incompatível com o ambiente Cloudflare Workers. A migração para PBKDF2 foi resolvida na ADR-0003.
Modos de sessão
| Modo | Configuração | Comportamento |
|---|---|---|
| Bearer | AUTH_SESSION_MODE=bearer (padrão) | Refresh token enviado no corpo da resposta; frontend armazena em IndexedDB |
| Cookie | AUTH_SESSION_MODE=cookie | Refresh token em cookie httpOnly; mutações exigem X-CSRF-Token |
Arquitetura de módulos
Backend (workers/src/)
| Arquivo | Responsabilidade |
|---|---|
modules/auth/password.ts | Hashing e verificação PBKDF2 |
modules/auth/sessionTokens.ts | Assinatura/verificação JWT + refresh token |
middleware/auth.ts | Middleware de verificação JWT + dev mode + role guard |
db/queries.ts | Queries reutilizáveis de auth e idempotência |
routes/auth.ts | Handlers de login, refresh, revoke |
Frontend (app/src/)
| Arquivo | Responsabilidade |
|---|---|
app/context/AuthContext.tsx | Estado de autenticação, papéis, sessão |
modules/auth/backendAuth.ts | Modo de autenticação via backend |
modules/auth/sessionManager.ts | Gerenciamento de sessão local |
modules/auth/sessionHydrator.ts | Hidratação da sessão a partir do IndexedDB |
modules/auth/keyRotationService.ts | Rotação de chaves de criptografia local |
Diagrama de sequência
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Frontend │ │ Backend │ │ D1 (SQLite) │
└────┬─────┘ └────┬─────┘ └──────┬───────┘
│ │ │
│ POST /auth/login │ │
│───────────────────▶│ │
│ │ SELECT password_hash│
│ │─────────────────────▶│
│ │◀─────────────────────│
│ │ │
│ │ PBKDF2.verify() │
│ │ │
│ │ INSERT session │
│ │─────────────────────▶│
│ │◀─────────────────────│
│ │ │
│ { accessToken, │ │
│ refreshToken, │ │
│ csrfToken, │ │
│ user } │ │
│◀───────────────────│ │
│ │ │
│ (15 min depois) │ │
│ │ │
│ POST /auth/refresh│ │
│───────────────────▶│ │
│ │ SELECT/UPDATE │
│ │ session (rotate) │
│ │─────────────────────▶│
│ │◀─────────────────────│
│ │ │
│ { newAccessToken, │ │
│ newRefreshToken }│ │
│◀───────────────────│ │
│ │ │
│ POST /auth/revoke │ │
│───────────────────▶│ │
│ │ UPDATE revoked_at │
│ │─────────────────────▶│
│ │◀─────────────────────│
│ { ok: true } │ │
│◀───────────────────│ │Decisões arquiteturais relacionadas
- ADR-0003: Auth Adapter and Session Token Model: Modelo de sessão com access token de curta duração, sessão local com TTL, e bloqueio de ações protegidas após expiração. Define que a implementação de auth deve ser encapsulada atrás de uma interface de módulo.
- ADR-0007: Keycloak token validation boundary: Backend valida tokens Bearer via JWKS e verificações de issuer/audience. Frontend mantém detalhes específicos do provedor dentro dos limites do adapter de auth.
Fontes: workers/CONTEXT.md, ADR-0003, ADR-0007