Files

80 lines
2.1 KiB
Markdown

# DH Inspección V2 — Fase A2
## Versión
```text
v0.2.1 · Fase A2 · Autenticación y sesiones
```
La leyenda se muestra en el footer del frontend. El health de la API también
informa `version: 0.2.1` y `phase: A2`.
## Endpoints
```text
POST /api/v3/auth/login
POST /api/v3/auth/refresh
POST /api/v3/auth/logout
GET /api/v3/auth/me
POST /api/v3/auth/change-password
```
`GET /api/v3/health` continúa siendo el único endpoint público sin datos de
negocio.
## Seguridad
- passwords con Argon2id;
- respuesta genérica para credenciales inválidas;
- 5 intentos fallidos y 15 minutos de bloqueo por defecto;
- rate limiting por IP;
- access token JWT HS256 corto;
- refresh token opaco, rotativo y persistido únicamente como HMAC-SHA256;
- cookies `HttpOnly`, `Secure` y `SameSite=Strict`;
- defensa CSRF de doble envío para operaciones mutables con cookies;
- validación estricta del origen web configurado;
- sesiones revocables y detección de reutilización;
- sanitización de passwords, hashes, tokens, cookies y secretos en auditoría;
- `X-Request-ID` en respuestas y eventos de auditoría.
El navegador no utiliza `localStorage` ni `sessionStorage` para tokens.
## Migración A2
```text
PhaseA2SessionSecurityIndexes1786548005000
```
Agrega índices para `users.locked_until` y
`auth_sessions.replaced_by_session_id`. No elimina ni transforma datos A1.
## Primer administrador
La CLI `npm run bootstrap:admin`:
- funciona únicamente en una terminal interactiva;
- oculta la contraseña mientras se escribe;
- exige entre 12 y 128 caracteres;
- evita crear un segundo administrador por bootstrap;
- usa una transacción y un advisory lock para impedir ejecuciones simultáneas;
- asigna el rol `admin` sembrado en A1;
- registra el evento `SYSTEM_BOOTSTRAP_ADMIN_CREATED`;
- nunca imprime la contraseña ni su hash.
## Variables nuevas
```text
WEB_ORIGIN
JWT_ACCESS_SECRET
REFRESH_TOKEN_PEPPER
ACCESS_TOKEN_TTL_SECONDS
REFRESH_TOKEN_TTL_SECONDS
AUTH_MAX_LOGIN_ATTEMPTS
AUTH_LOCKOUT_SECONDS
ACCESS_COOKIE_NAME
REFRESH_COOKIE_NAME
CSRF_COOKIE_NAME
```
Los dos secretos deben ser diferentes y tener al menos 64 caracteres.