specleap-framework 2.1.0 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.agents/backend.md +3 -3
  2. package/.agents/frontend.md +2 -2
  3. package/.agents/producto.md +9 -11
  4. package/.claude/hooks/spec-guard.sh +132 -0
  5. package/.claude/settings.json.template +15 -0
  6. package/.clinerules +3 -3
  7. package/.coderabbit.yaml +2 -4
  8. package/.commands/compliance.md +89 -0
  9. package/.commands/inicio.md +15 -15
  10. package/.commands/nuevo/README.md +2 -2
  11. package/.commands/planificar.md +1 -1
  12. package/.continue/rules/04-git-workflow.md +5 -5
  13. package/.continuerules +3 -4
  14. package/.cursorrules +1 -1
  15. package/.github/copilot-instructions.md +1 -1
  16. package/.specleap/i18n/en.json +177 -0
  17. package/.specleap/i18n/es.json +177 -0
  18. package/.specleap/i18n.sh +63 -0
  19. package/CHANGELOG.md +276 -0
  20. package/CLAUDE.md +54 -13
  21. package/README.md +169 -528
  22. package/SETUP.md +16 -13
  23. package/openspec/INDEX.md +53 -0
  24. package/openspec/README.md +104 -0
  25. package/openspec/SPEC-FORMAT.md +168 -0
  26. package/openspec/changes/.gitkeep +0 -0
  27. package/openspec/cli/COMMAND_REFERENCE.md +817 -0
  28. package/openspec/cli/README.md +189 -0
  29. package/openspec/cli/apply.sh +229 -0
  30. package/openspec/cli/archive.sh +240 -0
  31. package/openspec/cli/code-review.sh +207 -0
  32. package/openspec/cli/common.sh +171 -0
  33. package/openspec/cli/enrich.sh +188 -0
  34. package/openspec/cli/ff.sh +329 -0
  35. package/openspec/cli/new.sh +260 -0
  36. package/openspec/cli/openspec +82 -0
  37. package/openspec/cli/report.sh +244 -0
  38. package/openspec/cli/status.sh +178 -0
  39. package/openspec/cli/verify.sh +246 -0
  40. package/openspec/config.yaml +76 -0
  41. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/00-original-user-story.md +5 -0
  42. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/01-refined-user-story.md +106 -0
  43. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/README.md +333 -0
  44. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/design.md +461 -0
  45. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/proposal.md +124 -0
  46. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/functional/F001-authentication.spec.md +399 -0
  47. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/technical/T001-jwt-implementation.spec.md +606 -0
  48. package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/tasks.md +433 -0
  49. package/openspec/examples/MERMAID_DIAGRAMS.md +481 -0
  50. package/openspec/examples/README.md +334 -0
  51. package/openspec/specs/functional/.gitkeep +0 -0
  52. package/openspec/specs/integration/.gitkeep +0 -0
  53. package/openspec/specs/security/.gitkeep +0 -0
  54. package/openspec/specs/technical/.gitkeep +0 -0
  55. package/openspec/templates/.coderabbit.yaml +259 -0
  56. package/openspec/templates/design.md +181 -0
  57. package/openspec/templates/proposal.md +79 -0
  58. package/openspec/templates/tasks.md +193 -0
  59. package/package.json +10 -5
  60. package/rules/git-workflow.md +3 -3
  61. package/rules/session-protocol.md +3 -3
  62. package/scripts/README.md +13 -25
  63. package/scripts/compliance-audit.sh +325 -0
  64. package/scripts/generate-contract.sh +4 -4
  65. package/scripts/install-skills.sh +12 -11
  66. package/scripts/lib/render-contrato.py +1 -1
  67. package/scripts/quality-baseline.sh +210 -0
  68. package/scripts/quality-healing.sh +241 -0
  69. package/setup.sh +3 -3
  70. package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
  71. package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
  72. package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/search.cpython-314.pyc +0 -0
  73. package/scripts/lib/jira-project-utils.sh +0 -222
  74. package/scripts/setup-mcp.sh +0 -654
  75. package/scripts/test-cuestionario.sh +0 -428
@@ -0,0 +1,461 @@
1
+ # [CHANGE-SAMPLE-001] Diseño Técnico: Autenticación de Usuario
2
+
3
+ | Campo | Valor |
4
+ |-------|-------|
5
+ | ID | CHANGE-SAMPLE-001 |
6
+ | Propuesta | `proposal.md` |
7
+ | Fecha | 2026-02-12 |
8
+ | Estado | approved |
9
+ | Arquitecto | SpecLeap Contributor |
10
+
11
+ ## Resumen del Diseño
12
+
13
+ Sistema de autenticación basado en JWT con validación de credenciales contra base de datos, hashing bcrypt, rate limiting por IP, y middleware de autorización para rutas protegidas.
14
+
15
+ ## Arquitectura
16
+
17
+ ### Diagrama de Componentes
18
+
19
+ ```mermaid
20
+ flowchart TB
21
+ Cliente["Cliente<br/>(Browser)"] --> AuthController["AuthController<br/>(API Layer)"]
22
+ AuthController --> RateLimiter["RateLimitMiddleware<br/>(Brute Force Protection)"]
23
+ RateLimiter --> AuthService["AuthService<br/>(Business Logic)"]
24
+ AuthService --> UserRepo["UserRepository<br/>(Data Access)"]
25
+ AuthService --> JWTService["JWTService<br/>(Token Manager)"]
26
+ UserRepo --> DB[("Database<br/>users table")]
27
+ RateLimiter --> LoginAttempts[("Database<br/>login_attempts")]
28
+
29
+ style Cliente fill:#e1f5ff
30
+ style AuthController fill:#fff4e1
31
+ style RateLimiter fill:#ffe1e1
32
+ style AuthService fill:#e1ffe1
33
+ style JWTService fill:#f0e1ff
34
+ style UserRepo fill:#fff0e1
35
+ style DB fill:#e8e8e8
36
+ style LoginAttempts fill:#e8e8e8
37
+ ```
38
+
39
+ **Diagrama ASCII (alternativo):**
40
+ ```
41
+ ┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
42
+ │ Cliente │────▶│ AuthController │────▶│ AuthService │
43
+ │ (Browser) │◀────│ (API Layer) │◀────│ (Business Logic)│
44
+ └─────────────┘ └─────────────────┘ └─────────────────┘
45
+ │ │
46
+ │ ▼
47
+ │ ┌─────────────────┐
48
+ │ │ UserRepository │
49
+ │ │ (Data Layer) │
50
+ │ └─────────────────┘
51
+ │ │
52
+ ▼ ▼
53
+ ┌─────────────────┐ ┌─────────────────┐
54
+ │ RateLimiter │ │ Database │
55
+ │ (Middleware) │ │ (users table) │
56
+ └─────────────────┘ └─────────────────┘
57
+
58
+
59
+ ┌─────────────────┐
60
+ │ JWTService │
61
+ │ (Token Manager) │
62
+ └─────────────────┘
63
+ ```
64
+
65
+ ### Componentes Afectados
66
+
67
+ | Componente | Tipo de Cambio | Descripción |
68
+ |------------|----------------|-------------|
69
+ | AuthController | Nuevo | Controlador de endpoints de autenticación |
70
+ | AuthService | Nuevo | Lógica de negocio de autenticación |
71
+ | UserRepository | Nuevo | Acceso a datos de usuarios |
72
+ | JWTService | Nuevo | Generación y validación de tokens |
73
+ | AuthMiddleware | Nuevo | Middleware de autorización para rutas |
74
+ | RateLimitMiddleware | Nuevo | Protección contra brute force |
75
+ | routes/api.php | Modificado | Registro de rutas de autenticación |
76
+
77
+ ## Modelo de Datos
78
+
79
+ ### Tabla Existente: `users`
80
+
81
+ ```sql
82
+ CREATE TABLE users (
83
+ id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
84
+ email VARCHAR(255) UNIQUE NOT NULL,
85
+ password_hash VARCHAR(255) NOT NULL,
86
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
87
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
88
+ INDEX idx_email (email)
89
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
90
+ ```
91
+
92
+ ### Nueva Tabla: `login_attempts`
93
+
94
+ ```sql
95
+ CREATE TABLE login_attempts (
96
+ id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
97
+ email VARCHAR(255) NOT NULL,
98
+ ip_address VARCHAR(45) NOT NULL,
99
+ user_agent TEXT,
100
+ success BOOLEAN DEFAULT FALSE,
101
+ attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
102
+ INDEX idx_email_ip (email, ip_address),
103
+ INDEX idx_attempted_at (attempted_at)
104
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
105
+ ```
106
+
107
+ ### Migraciones de Base de Datos
108
+
109
+ ```sql
110
+ -- Migration: 20260212000001_create_login_attempts_table.sql
111
+ CREATE TABLE login_attempts (
112
+ id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
113
+ email VARCHAR(255) NOT NULL,
114
+ ip_address VARCHAR(45) NOT NULL,
115
+ user_agent TEXT,
116
+ success BOOLEAN DEFAULT FALSE,
117
+ attempted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
118
+ INDEX idx_email_ip (email, ip_address),
119
+ INDEX idx_attempted_at (attempted_at)
120
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
121
+ ```
122
+
123
+ ## API / Interfaces
124
+
125
+ ### Nuevos Endpoints
126
+
127
+ ```
128
+ POST /api/v1/auth/login
129
+ POST /api/v1/auth/logout
130
+ POST /api/v1/auth/refresh
131
+ GET /api/v1/auth/me
132
+ ```
133
+
134
+ ### Contratos de API
135
+
136
+ #### POST /api/v1/auth/login
137
+
138
+ **Request:**
139
+ ```json
140
+ {
141
+ "email": "usuario@example.com",
142
+ "password": "contraseña_segura",
143
+ "remember": false
144
+ }
145
+ ```
146
+
147
+ **Response (200 OK):**
148
+ ```json
149
+ {
150
+ "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
151
+ "token_type": "Bearer",
152
+ "expires_in": 86400,
153
+ "user": {
154
+ "id": 123,
155
+ "email": "usuario@example.com"
156
+ }
157
+ }
158
+ ```
159
+
160
+ **Response (401 Unauthorized):**
161
+ ```json
162
+ {
163
+ "error": "Credenciales inválidas"
164
+ }
165
+ ```
166
+
167
+ **Response (429 Too Many Requests):**
168
+ ```json
169
+ {
170
+ "error": "Cuenta temporalmente bloqueada por intentos fallidos. Intente nuevamente en 30 minutos."
171
+ }
172
+ ```
173
+
174
+ #### POST /api/v1/auth/logout
175
+
176
+ **Headers:**
177
+ ```
178
+ Authorization: Bearer {token}
179
+ ```
180
+
181
+ **Response (200 OK):**
182
+ ```json
183
+ {
184
+ "message": "Sesión cerrada exitosamente"
185
+ }
186
+ ```
187
+
188
+ #### GET /api/v1/auth/me
189
+
190
+ **Headers:**
191
+ ```
192
+ Authorization: Bearer {token}
193
+ ```
194
+
195
+ **Response (200 OK):**
196
+ ```json
197
+ {
198
+ "id": 123,
199
+ "email": "usuario@example.com",
200
+ "created_at": "2026-01-15T10:30:00Z"
201
+ }
202
+ ```
203
+
204
+ ## Flujo de Datos
205
+
206
+ ### Secuencia Principal: Login Exitoso
207
+
208
+ ```mermaid
209
+ sequenceDiagram
210
+ actor Usuario
211
+ participant Cliente as Cliente<br/>(Browser)
212
+ participant Controller as AuthController
213
+ participant RateLimit as RateLimitMiddleware
214
+ participant Service as AuthService
215
+ participant Repo as UserRepository
216
+ participant JWT as JWTService
217
+ participant DB as Database
218
+
219
+ Usuario->>Cliente: Ingresa email + password
220
+ Cliente->>Controller: POST /api/v1/auth/login
221
+ Controller->>RateLimit: Verificar intentos
222
+ RateLimit->>DB: Contar intentos fallidos (15min)
223
+ DB-->>RateLimit: 2 intentos
224
+ RateLimit-->>Controller: ✓ OK (< 5 intentos)
225
+
226
+ Controller->>Service: authenticate(email, password)
227
+ Service->>Repo: findByEmail(email)
228
+ Repo->>DB: SELECT * FROM users WHERE email=?
229
+ DB-->>Repo: User found
230
+ Repo-->>Service: User object
231
+
232
+ Service->>Service: Hash.check(password, user.password_hash)
233
+ Note over Service: bcrypt verification ~250ms
234
+
235
+ Service->>JWT: generateToken(user, remember=false)
236
+ JWT->>JWT: Sign with RS256 private key
237
+ JWT-->>Service: JWT Token (24h expiry)
238
+
239
+ Service->>DB: INSERT INTO login_attempts<br/>(email, ip, success=true)
240
+
241
+ Service-->>Controller: {token, user}
242
+ Controller-->>Cliente: 200 OK {token, token_type, expires_in, user}
243
+ Cliente-->>Usuario: Redirigir a Dashboard
244
+ ```
245
+
246
+ ### Casos de Error
247
+
248
+ | Código | Escenario | Respuesta |
249
+ |--------|-----------|-----------|
250
+ | 400 | Email/password faltantes | `{ error: "Email y contraseña son requeridos" }` |
251
+ | 401 | Credenciales inválidas | `{ error: "Credenciales inválidas" }` |
252
+ | 429 | Cuenta bloqueada | `{ error: "Cuenta temporalmente bloqueada..." }` |
253
+ | 500 | Error interno (DB, etc.) | `{ error: "Error del servidor, intente más tarde" }` |
254
+
255
+ ## Seguridad
256
+
257
+ ### Autenticación/Autorización
258
+
259
+ - **Método de autenticación:** JWT con firma RS256
260
+ - **Duración del token:** 24 horas (normal) / 30 días (remember me)
261
+ - **Roles con acceso:** Ninguno (endpoint público)
262
+ - **Rutas protegidas:** Aplicar `AuthMiddleware` a rutas que requieran autenticación
263
+
264
+ ### Validación de Inputs
265
+
266
+ - **email:** string, formato email válido, max 255 chars, sanitizado
267
+ - **password:** string, min 8 chars, max 255 chars, no sanitizado (hash directo)
268
+ - **remember:** boolean, opcional
269
+
270
+ ### Hashing de Contraseñas
271
+
272
+ ```php
273
+ // Registro (no en este CHANGE, pero para contexto)
274
+ $hash = Hash::make($password); // bcrypt cost=12
275
+
276
+ // Login
277
+ Hash::check($password, $user->password_hash);
278
+ ```
279
+
280
+ ### Rate Limiting
281
+
282
+ - **Límite:** 5 intentos fallidos por IP + email en 15 minutos
283
+ - **Bloqueo:** 30 minutos
284
+ - **Desbloqueo:** Automático tras expiración
285
+ - **Override manual:** Admin puede desbloquear desde DB
286
+
287
+ ### Consideraciones de Seguridad
288
+
289
+ - [x] CSRF protection (tokens en formularios web)
290
+ - [x] Rate limiting (brute force protection)
291
+ - [x] Input sanitization (email)
292
+ - [x] SQL injection prevention (ORM/prepared statements)
293
+ - [x] XSS prevention (no renderizar inputs sin escapar)
294
+ - [x] HTTPS obligatorio en producción
295
+ - [x] Headers de seguridad:
296
+ - `X-Content-Type-Options: nosniff`
297
+ - `X-Frame-Options: DENY`
298
+ - `Content-Security-Policy: default-src 'self'`
299
+ - [x] Logging de intentos para auditoría
300
+ - [x] Tokens HttpOnly (no accesibles desde JavaScript)
301
+
302
+ ## Performance
303
+
304
+ ### Estimaciones de Carga
305
+
306
+ - **Requests esperados:** 50 logins/minuto en hora pico
307
+ - **Usuarios concurrentes:** ~100 logins simultáneos
308
+ - **Tamaño de respuesta:** ~500 bytes (JSON + token)
309
+
310
+ ### Optimizaciones
311
+
312
+ #### Índices de Base de Datos
313
+
314
+ ```sql
315
+ -- Ya existente en users
316
+ INDEX idx_email (email)
317
+
318
+ -- Nuevos en login_attempts
319
+ INDEX idx_email_ip (email, ip_address)
320
+ INDEX idx_attempted_at (attempted_at)
321
+ ```
322
+
323
+ #### Caché Strategy
324
+
325
+ - **Validación de tokens:** Cachear public key para verificación (Redis, 1 hora)
326
+ - **Rate limiting counters:** Redis con TTL de 15 minutos
327
+ - **No cachear:** Credenciales, tokens generados
328
+
329
+ #### Paginación
330
+
331
+ No aplica (endpoints no devuelven listas).
332
+
333
+ ## Testing Strategy
334
+
335
+ ### Unit Tests
336
+
337
+ - [x] **AuthService:**
338
+ - `test_authenticate_with_valid_credentials()`
339
+ - `test_authenticate_with_invalid_password()`
340
+ - `test_authenticate_with_nonexistent_email()`
341
+ - [x] **JWTService:**
342
+ - `test_generate_token_with_user()`
343
+ - `test_validate_valid_token()`
344
+ - `test_validate_expired_token()`
345
+ - `test_validate_tampered_token()`
346
+ - [x] **RateLimitMiddleware:**
347
+ - `test_allows_requests_under_limit()`
348
+ - `test_blocks_requests_over_limit()`
349
+ - `test_resets_counter_after_timeout()`
350
+
351
+ ### Integration Tests
352
+
353
+ - [x] **POST /api/v1/auth/login:**
354
+ - `test_login_success_with_valid_credentials()`
355
+ - `test_login_fails_with_invalid_password()`
356
+ - `test_login_fails_with_nonexistent_email()`
357
+ - `test_login_blocked_after_5_failed_attempts()`
358
+ - `test_login_with_remember_me_extends_token_expiry()`
359
+ - [x] **POST /api/v1/auth/logout:**
360
+ - `test_logout_invalidates_token()`
361
+ - `test_logout_fails_without_token()`
362
+ - [x] **GET /api/v1/auth/me:**
363
+ - `test_returns_user_with_valid_token()`
364
+ - `test_fails_with_expired_token()`
365
+
366
+ ### E2E Tests (si aplica)
367
+
368
+ - [x] Flujo completo: Login → Acceder a ruta protegida → Logout
369
+ - [x] Flujo de bloqueo: 5 intentos fallidos → Bloqueo → Espera 30min → Reintento exitoso
370
+
371
+ ## Plan de Rollback
372
+
373
+ ### Pasos para Revertir
374
+
375
+ 1. **Código:**
376
+ ```bash
377
+ git revert <commit-hash>
378
+ git push origin main
379
+ ```
380
+
381
+ 2. **Migración de BD:**
382
+ ```sql
383
+ DROP TABLE IF EXISTS login_attempts;
384
+ ```
385
+
386
+ 3. **Caché:**
387
+ ```bash
388
+ redis-cli FLUSHDB
389
+ ```
390
+
391
+ 4. **Rutas protegidas:** Comentar `AuthMiddleware` temporalmente
392
+
393
+ ### Datos a Preservar
394
+
395
+ - **Tabla `users`:** NO tocar (ya existente)
396
+ - **Tabla `login_attempts`:** Puede eliminarse (solo auditoría)
397
+ - **Tokens activos:** Se invalidarán automáticamente al revertir
398
+
399
+ ## Decisiones Técnicas (ADR)
400
+
401
+ ### ADR-001: JWT con RS256 en lugar de HS256
402
+
403
+ **Contexto:** Necesitamos decidir algoritmo de firma de tokens JWT.
404
+
405
+ **Alternativas:**
406
+ - **HS256:** Firma simétrica (misma clave para generar y validar)
407
+ - **RS256:** Firma asimétrica (clave privada para generar, pública para validar)
408
+
409
+ **Decisión:** Elegimos **RS256**.
410
+
411
+ **Razones:**
412
+ - Permite validación de tokens en servicios sin acceso a clave privada
413
+ - Mayor seguridad (comprometer clave pública no permite generar tokens falsos)
414
+ - Estándar en sistemas distribuidos
415
+
416
+ **Consecuencias:**
417
+ - ✅ Mejor seguridad y escalabilidad
418
+ - ⚠️ Ligeramente más lento que HS256 (despreciable en nuestra carga)
419
+ - ⚠️ Requiere gestión de par de claves RSA
420
+
421
+ ### ADR-002: Bcrypt cost factor = 12
422
+
423
+ **Contexto:** Definir balance entre seguridad y performance para hashing.
424
+
425
+ **Decisión:** Bcrypt con cost factor **12**.
426
+
427
+ **Razones:**
428
+ - OWASP recomienda mínimo 10, ideal 12-14
429
+ - Cost 12 toma ~250ms en hardware moderno (aceptable para login)
430
+ - Protege contra GPUs de ataques offline
431
+
432
+ **Consecuencias:**
433
+ - ✅ Resistencia fuerte contra brute force offline
434
+ - ⚠️ ~250ms por hash (solo en login/registro, no es problema)
435
+
436
+ ### ADR-003: Rate limiting por IP + Email
437
+
438
+ **Contexto:** Decidir granularidad del rate limiting.
439
+
440
+ **Alternativas:**
441
+ - Solo por IP
442
+ - Solo por email
443
+ - IP + email
444
+
445
+ **Decisión:** **IP + email** combinados.
446
+
447
+ **Razones:**
448
+ - Previene ataques distribuidos (múltiples IPs a mismo email)
449
+ - Previene bloqueo global de una IP compartida
450
+
451
+ **Consecuencias:**
452
+ - ✅ Protección más robusta
453
+ - ⚠️ Requiere índice compuesto en `login_attempts`
454
+
455
+ ## Referencias
456
+
457
+ - Propuesta: `proposal.md`
458
+ - Spec principal: `openspec/specs/functional/F001-authentication.spec.md`
459
+ - Delta specs: `specs/functional/F001-authentication.spec.md`
460
+ - JWT RFC: https://tools.ietf.org/html/rfc7519
461
+ - OWASP Auth Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
@@ -0,0 +1,124 @@
1
+ # [CHANGE-SAMPLE-001] Propuesta: Implementar Autenticación de Usuario
2
+
3
+ | Campo | Valor |
4
+ |-------|-------|
5
+ | ID | CHANGE-SAMPLE-001 |
6
+ | Fecha | 2026-02-12 |
7
+ | Estado | completed |
8
+ | Autor | SpecLeap Contributor |
9
+ | Prioridad | high |
10
+ | Ticket | app-tienda-23 |
11
+
12
+ ## Resumen Ejecutivo
13
+
14
+ Implementar sistema de autenticación seguro con email y contraseña, generación de tokens JWT, y protección contra ataques de fuerza bruta. Base fundamental para todas las funcionalidades autenticadas del sistema.
15
+
16
+ ## Contexto y Problema
17
+
18
+ ### Situación Actual
19
+ El sistema actualmente no tiene ningún mecanismo de autenticación. Todas las rutas son públicas y no hay forma de identificar usuarios ni proteger datos personales.
20
+
21
+ ### Problema a Resolver
22
+ - **P1:** Exposición de datos sensibles sin control de acceso
23
+ - **P2:** Imposibilidad de personalizar funcionalidades por usuario
24
+ - **P3:** Incumplimiento de regulaciones de protección de datos (GDPR, etc.)
25
+ - **P4:** No hay trazabilidad de acciones por usuario
26
+
27
+ ### Impacto si No se Resuelve
28
+ - Violaciones de seguridad y privacidad
29
+ - Imposibilidad de lanzar funcionalidades que requieran identificación
30
+ - Incumplimiento legal
31
+ - Pérdida de confianza de usuarios
32
+
33
+ ## Propuesta de Solución
34
+
35
+ ### Descripción General
36
+ Implementar sistema de autenticación basado en JWT (JSON Web Tokens) con:
37
+ - Endpoint de login que valida credenciales
38
+ - Generación de tokens firmados
39
+ - Middleware de autenticación para rutas protegidas
40
+ - Rate limiting y protección contra brute force
41
+ - Logging y auditoría completos
42
+
43
+ ### User Stories Afectadas
44
+
45
+ #### US-AUTH-001: Como usuario registrado, quiero autenticarme con email y contraseña
46
+ - Criterios de aceptación:
47
+ - [x] CA-01: Login exitoso con credenciales válidas genera token JWT
48
+ - [x] CA-02: Login fallido muestra error genérico sin revelar información
49
+ - [x] CA-03: Cuenta bloqueada temporalmente tras 5 intentos fallidos
50
+ - [x] CA-04: Email no registrado muestra error genérico
51
+ - [x] CA-05: Token válido por 24h (o 30 días con "Remember Me")
52
+ - [x] CA-06: Logout invalida token actual
53
+ - [x] CA-07: Token expirado redirige a login
54
+
55
+ ## Alcance
56
+
57
+ ### Incluido
58
+ - Endpoint POST `/api/v1/auth/login`
59
+ - Endpoint POST `/api/v1/auth/logout`
60
+ - Middleware `AuthMiddleware` para proteger rutas
61
+ - Rate limiting (5 intentos/15min por IP)
62
+ - Hashing de contraseñas con bcrypt
63
+ - Generación de tokens JWT con RS256
64
+ - Logging de intentos de login
65
+ - Tests unitarios e integración (>80% coverage)
66
+ - Documentación de API (OpenAPI)
67
+
68
+ ### Excluido (fuera de alcance)
69
+ - OAuth / Social login (propuesta futura: CHANGE-002)
70
+ - 2FA / MFA (propuesta futura: CHANGE-003)
71
+ - Recuperación de contraseña (propuesta futura: CHANGE-004)
72
+ - Registro de usuarios (propuesta futura: CHANGE-005)
73
+ - SSO (propuesta futura: CHANGE-006)
74
+
75
+ ## Riesgos y Mitigaciones
76
+
77
+ | Riesgo | Probabilidad | Impacto | Mitigación |
78
+ |--------|--------------|---------|------------|
79
+ | Vulnerabilidad en implementación JWT | Media | Alto | Code review exhaustivo + herramientas SAST (Semgrep) |
80
+ | Bloqueo de usuarios legítimos | Media | Medio | Rate limiting configurable + desbloqueo manual |
81
+ | Performance bajo alta carga | Baja | Medio | Cachear validación de tokens + load testing |
82
+ | Tokens comprometidos | Baja | Alto | Expiración corta + rotación periódica |
83
+
84
+ ## Dependencias
85
+
86
+ - [x] Tabla `users` con campos `email`, `password_hash`, `created_at`, `updated_at`
87
+ - [x] Librería JWT (tymon/jwt-auth para Laravel o jsonwebtoken para Node)
88
+ - [ ] Configuración de claves RSA para firma de tokens (bloqueante)
89
+ - [ ] Sistema de logging centralizado (no bloqueante)
90
+
91
+ ## Estimación Inicial
92
+
93
+ | Concepto | Estimación |
94
+ |----------|------------|
95
+ | Esfuerzo | M (2 sprints) |
96
+ | Tiempo | 2 semanas |
97
+ | Complejidad | Alta |
98
+
99
+ Desglose:
100
+ - Backend (API + middleware): 5 días
101
+ - Tests: 2 días
102
+ - Seguridad (rate limiting, auditoría): 2 días
103
+ - Documentación: 1 día
104
+
105
+ ## Decisión
106
+
107
+ - [x] **Aprobada** — Fecha: 2026-02-12
108
+ - Prioridad: High (bloqueante para features autenticadas)
109
+ - Aprobado por: Tech Lead + Product Owner
110
+
111
+ ## Referencias
112
+
113
+ - Spec relacionada: `openspec/specs/functional/F001-authentication.spec.md`
114
+ - Delta spec: `specs/functional/F001-authentication.spec.md`
115
+ - Diseño: `design.md`
116
+ - Tareas: `tasks.md`
117
+ - User story refinada: `01-refined-user-story.md`
118
+
119
+ ---
120
+
121
+ ## Completada
122
+ - Fecha: 2026-02-12
123
+ - PR: #45 (merged)
124
+ - Deploy: Production v1.1.0