80 lines
2.1 KiB
Markdown
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.
|