specleap-framework 2.1.1 → 2.1.6
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.
- package/.agents/backend.md +3 -3
- package/.agents/frontend.md +2 -2
- package/.agents/producto.md +9 -11
- package/.claude/hooks/spec-guard.sh +132 -0
- package/.claude/settings.json.template +15 -0
- package/.clinerules +3 -3
- package/.coderabbit.yaml +2 -4
- package/.commands/compliance.md +89 -0
- package/.commands/inicio.md +15 -15
- package/.commands/nuevo/README.md +2 -2
- package/.commands/planificar.md +1 -1
- package/.continue/rules/04-git-workflow.md +5 -5
- package/.continuerules +3 -4
- package/.cursorrules +1 -1
- package/.github/copilot-instructions.md +1 -1
- package/.specleap/i18n/en.json +177 -0
- package/.specleap/i18n/es.json +177 -0
- package/.specleap/i18n.sh +63 -0
- package/CHANGELOG.md +292 -0
- package/CLAUDE.md +54 -13
- package/README.md +169 -529
- package/SETUP.md +16 -13
- package/openspec/INDEX.md +53 -0
- package/openspec/README.md +104 -0
- package/openspec/SPEC-FORMAT.md +168 -0
- package/openspec/changes/.gitkeep +0 -0
- package/openspec/cli/COMMAND_REFERENCE.md +817 -0
- package/openspec/cli/README.md +189 -0
- package/openspec/cli/apply.sh +229 -0
- package/openspec/cli/archive.sh +240 -0
- package/openspec/cli/code-review.sh +207 -0
- package/openspec/cli/common.sh +171 -0
- package/openspec/cli/enrich.sh +188 -0
- package/openspec/cli/ff.sh +329 -0
- package/openspec/cli/new.sh +260 -0
- package/openspec/cli/openspec +82 -0
- package/openspec/cli/report.sh +244 -0
- package/openspec/cli/status.sh +178 -0
- package/openspec/cli/verify.sh +246 -0
- package/openspec/config.yaml +76 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/00-original-user-story.md +5 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/01-refined-user-story.md +106 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/README.md +333 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/design.md +461 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/proposal.md +124 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/functional/F001-authentication.spec.md +399 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/technical/T001-jwt-implementation.spec.md +606 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/tasks.md +433 -0
- package/openspec/examples/MERMAID_DIAGRAMS.md +481 -0
- package/openspec/examples/README.md +334 -0
- package/openspec/specs/functional/.gitkeep +0 -0
- package/openspec/specs/integration/.gitkeep +0 -0
- package/openspec/specs/security/.gitkeep +0 -0
- package/openspec/specs/technical/.gitkeep +0 -0
- package/openspec/templates/.coderabbit.yaml +259 -0
- package/openspec/templates/design.md +181 -0
- package/openspec/templates/proposal.md +79 -0
- package/openspec/templates/tasks.md +193 -0
- package/package.json +13 -6
- package/rules/git-workflow.md +3 -3
- package/rules/session-protocol.md +3 -3
- package/scripts/README.md +13 -25
- package/scripts/compliance-audit.sh +325 -0
- package/scripts/generate-contract.sh +4 -4
- package/scripts/install-skills.sh +8 -8
- package/scripts/lib/render-contrato.py +1 -1
- package/scripts/quality-baseline.sh +210 -0
- package/scripts/quality-healing.sh +241 -0
- package/setup.sh +3 -3
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/search.cpython-314.pyc +0 -0
- package/scripts/lib/jira-project-utils.sh +0 -222
- package/scripts/setup-mcp.sh +0 -654
- package/scripts/test-cuestionario.sh +0 -428
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# [CHANGE-SAMPLE-001] Tareas: Autenticación de Usuario
|
|
2
|
+
|
|
3
|
+
| Campo | Valor |
|
|
4
|
+
|-------|-------|
|
|
5
|
+
| ID | CHANGE-SAMPLE-001 |
|
|
6
|
+
| Propuesta | `proposal.md` |
|
|
7
|
+
| Diseño | `design.md` |
|
|
8
|
+
| Fecha | 2026-02-12 |
|
|
9
|
+
| Sprint | Sprint 5 |
|
|
10
|
+
|
|
11
|
+
## Resumen
|
|
12
|
+
|
|
13
|
+
Total de tareas: 12
|
|
14
|
+
Estimación total: 21 story points
|
|
15
|
+
Ticket Epic: app-tienda-23
|
|
16
|
+
|
|
17
|
+
## Tareas
|
|
18
|
+
|
|
19
|
+
### 🏗️ Setup & Scaffolding
|
|
20
|
+
|
|
21
|
+
#### TASK-001: Crear migración de login_attempts
|
|
22
|
+
- **Ticket:** app-tienda-24
|
|
23
|
+
- **Estimación:** 1 SP
|
|
24
|
+
- **Asignado:** @backend-dev
|
|
25
|
+
- **Estado:** ✅ Completada
|
|
26
|
+
- **Branch:** `feat/CHANGE-001-login-attempts-table`
|
|
27
|
+
|
|
28
|
+
**Subtareas:**
|
|
29
|
+
- [x] Crear migración `20260212000001_create_login_attempts_table`
|
|
30
|
+
- [x] Definir índices (email+ip, attempted_at)
|
|
31
|
+
- [x] Ejecutar migración en dev
|
|
32
|
+
- [x] Crear seeder para testing
|
|
33
|
+
|
|
34
|
+
**Criterios de aceptación:**
|
|
35
|
+
- [x] Migración ejecuta sin errores
|
|
36
|
+
- [x] Índices creados correctamente
|
|
37
|
+
- [x] Rollback funciona
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
#### TASK-002: Generar par de claves RSA para JWT
|
|
42
|
+
- **Ticket:** app-tienda-25
|
|
43
|
+
- **Estimación:** 1 SP
|
|
44
|
+
- **Asignado:** @devops
|
|
45
|
+
- **Estado:** ✅ Completada
|
|
46
|
+
- **Branch:** N/A (config)
|
|
47
|
+
|
|
48
|
+
**Subtareas:**
|
|
49
|
+
- [x] Generar private key (4096 bits)
|
|
50
|
+
- [x] Extraer public key
|
|
51
|
+
- [x] Configurar paths en .env
|
|
52
|
+
- [x] Añadir a .gitignore
|
|
53
|
+
- [x] Documentar rotación de claves
|
|
54
|
+
|
|
55
|
+
**Criterios de aceptación:**
|
|
56
|
+
- [x] Claves generadas y almacenadas de forma segura
|
|
57
|
+
- [x] Variables de entorno configuradas
|
|
58
|
+
- [x] No commitear claves privadas
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### 📊 Backend / API
|
|
63
|
+
|
|
64
|
+
#### TASK-003: Implementar UserRepository
|
|
65
|
+
- **Ticket:** app-tienda-26
|
|
66
|
+
- **Estimación:** 2 SP
|
|
67
|
+
- **Asignado:** @backend-dev
|
|
68
|
+
- **Estado:** ✅ Completada
|
|
69
|
+
- **Branch:** `feat/CHANGE-001-user-repository`
|
|
70
|
+
|
|
71
|
+
**Subtareas:**
|
|
72
|
+
- [x] Crear `UserRepository` class
|
|
73
|
+
- [x] Método `findByEmail(email)`
|
|
74
|
+
- [x] Método `findById(id)`
|
|
75
|
+
- [x] Manejo de excepciones
|
|
76
|
+
|
|
77
|
+
**Criterios de aceptación:**
|
|
78
|
+
- [x] Repository implementado según patrón
|
|
79
|
+
- [x] Query optimizada con índices
|
|
80
|
+
- [x] Tests unitarios (coverage >90%)
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
#### TASK-004: Implementar JWTService
|
|
85
|
+
- **Ticket:** app-tienda-27
|
|
86
|
+
- **Estimación:** 3 SP
|
|
87
|
+
- **Asignado:** @backend-dev
|
|
88
|
+
- **Estado:** ✅ Completada
|
|
89
|
+
- **Branch:** `feat/CHANGE-001-jwt-service`
|
|
90
|
+
|
|
91
|
+
**Subtareas:**
|
|
92
|
+
- [x] Crear `JWTService` class
|
|
93
|
+
- [x] Método `generateToken(user, remember=false)`
|
|
94
|
+
- [x] Método `validateToken(token)`
|
|
95
|
+
- [x] Método `refreshToken(token)`
|
|
96
|
+
- [x] Configurar RS256 con claves RSA
|
|
97
|
+
|
|
98
|
+
**Criterios de aceptación:**
|
|
99
|
+
- [x] Tokens firmados con RS256
|
|
100
|
+
- [x] Expiración correcta (24h / 30 días)
|
|
101
|
+
- [x] Validación detecta tokens expirados/tampered
|
|
102
|
+
- [x] Tests unitarios completos
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
#### TASK-005: Implementar AuthService
|
|
107
|
+
- **Ticket:** app-tienda-28
|
|
108
|
+
- **Estimación:** 3 SP
|
|
109
|
+
- **Asignado:** @backend-dev
|
|
110
|
+
- **Estado:** ✅ Completada
|
|
111
|
+
- **Branch:** `feat/CHANGE-001-auth-service`
|
|
112
|
+
|
|
113
|
+
**Subtareas:**
|
|
114
|
+
- [x] Crear `AuthService` class
|
|
115
|
+
- [x] Método `authenticate(email, password)`
|
|
116
|
+
- [x] Integrar `UserRepository` y `JWTService`
|
|
117
|
+
- [x] Hash verification con bcrypt
|
|
118
|
+
|
|
119
|
+
**Criterios de aceptación:**
|
|
120
|
+
- [x] Autenticación exitosa con credenciales válidas
|
|
121
|
+
- [x] Fallo con credenciales inválidas
|
|
122
|
+
- [x] No revela información sobre existencia de email
|
|
123
|
+
- [x] Tests unitarios (coverage >85%)
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
#### TASK-006: Implementar RateLimitMiddleware
|
|
128
|
+
- **Ticket:** app-tienda-29
|
|
129
|
+
- **Estimación:** 2 SP
|
|
130
|
+
- **Asignado:** @backend-dev
|
|
131
|
+
- **Estado:** ✅ Completada
|
|
132
|
+
- **Branch:** `feat/CHANGE-001-rate-limiting`
|
|
133
|
+
|
|
134
|
+
**Subtareas:**
|
|
135
|
+
- [x] Crear middleware `RateLimitMiddleware`
|
|
136
|
+
- [x] Registrar intentos en `login_attempts`
|
|
137
|
+
- [x] Contar intentos fallidos (15min window)
|
|
138
|
+
- [x] Bloquear tras 5 intentos
|
|
139
|
+
- [x] Configurar TTL de 30 minutos
|
|
140
|
+
|
|
141
|
+
**Criterios de aceptación:**
|
|
142
|
+
- [x] Bloqueo automático tras 5 intentos
|
|
143
|
+
- [x] Desbloqueo automático tras 30min
|
|
144
|
+
- [x] Mensajes de error claros
|
|
145
|
+
- [x] Tests de rate limiting
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
#### TASK-007: Implementar AuthController
|
|
150
|
+
- **Ticket:** app-tienda-30
|
|
151
|
+
- **Estimación:** 3 SP
|
|
152
|
+
- **Asignado:** @backend-dev
|
|
153
|
+
- **Estado:** ✅ Completada
|
|
154
|
+
- **Branch:** `feat/CHANGE-001-auth-controller`
|
|
155
|
+
|
|
156
|
+
**Subtareas:**
|
|
157
|
+
- [x] POST /api/v1/auth/login
|
|
158
|
+
- [x] POST /api/v1/auth/logout
|
|
159
|
+
- [x] POST /api/v1/auth/refresh
|
|
160
|
+
- [x] GET /api/v1/auth/me
|
|
161
|
+
- [x] Validación de inputs
|
|
162
|
+
- [x] Logging de intentos
|
|
163
|
+
|
|
164
|
+
**Criterios de aceptación:**
|
|
165
|
+
- [x] Endpoints responden según contratos en design.md
|
|
166
|
+
- [x] Validación de inputs correcta
|
|
167
|
+
- [x] Manejo de errores (400, 401, 429, 500)
|
|
168
|
+
- [x] Logging completo
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
#### TASK-008: Implementar AuthMiddleware
|
|
173
|
+
- **Ticket:** app-tienda-31
|
|
174
|
+
- **Estimación:** 2 SP
|
|
175
|
+
- **Asignado:** @backend-dev
|
|
176
|
+
- **Estado:** ✅ Completada
|
|
177
|
+
- **Branch:** `feat/CHANGE-001-auth-middleware`
|
|
178
|
+
|
|
179
|
+
**Subtareas:**
|
|
180
|
+
- [x] Crear middleware `AuthMiddleware`
|
|
181
|
+
- [x] Extraer token de header Authorization
|
|
182
|
+
- [x] Validar token con `JWTService`
|
|
183
|
+
- [x] Adjuntar user a request
|
|
184
|
+
- [x] Manejar tokens expirados/inválidos
|
|
185
|
+
|
|
186
|
+
**Criterios de aceptación:**
|
|
187
|
+
- [x] Middleware valida tokens correctamente
|
|
188
|
+
- [x] Rechaza requests sin token (401)
|
|
189
|
+
- [x] Rechaza tokens expirados (401)
|
|
190
|
+
- [x] User disponible en request
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### 🧪 Testing
|
|
195
|
+
|
|
196
|
+
#### TASK-009: Tests unitarios
|
|
197
|
+
- **Ticket:** app-tienda-32
|
|
198
|
+
- **Estimación:** 2 SP
|
|
199
|
+
- **Asignado:** @backend-dev
|
|
200
|
+
- **Estado:** ✅ Completada
|
|
201
|
+
- **Branch:** Incluido en branches de features
|
|
202
|
+
|
|
203
|
+
**Cobertura alcanzada:**
|
|
204
|
+
- [x] AuthService: 92%
|
|
205
|
+
- [x] JWTService: 95%
|
|
206
|
+
- [x] UserRepository: 88%
|
|
207
|
+
- [x] RateLimitMiddleware: 90%
|
|
208
|
+
|
|
209
|
+
**Total:** 45 unit tests, 45 passed, 0 failed
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
#### TASK-010: Tests de integración
|
|
214
|
+
- **Ticket:** app-tienda-33
|
|
215
|
+
- **Estimación:** 2 SP
|
|
216
|
+
- **Asignado:** @backend-dev
|
|
217
|
+
- **Estado:** ✅ Completada
|
|
218
|
+
- **Branch:** `feat/CHANGE-001-integration-tests`
|
|
219
|
+
|
|
220
|
+
**Escenarios cubiertos:**
|
|
221
|
+
- [x] Login exitoso con credenciales válidas
|
|
222
|
+
- [x] Login fallido con contraseña incorrecta
|
|
223
|
+
- [x] Login fallido con email no registrado
|
|
224
|
+
- [x] Bloqueo tras 5 intentos fallidos
|
|
225
|
+
- [x] Logout invalida token
|
|
226
|
+
- [x] /me con token válido
|
|
227
|
+
- [x] /me con token expirado
|
|
228
|
+
|
|
229
|
+
**Total:** 12 integration tests, 12 passed, 0 failed
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### 📚 Documentación
|
|
234
|
+
|
|
235
|
+
#### TASK-011: Documentación de API (OpenAPI)
|
|
236
|
+
- **Ticket:** app-tienda-34
|
|
237
|
+
- **Estimación:** 1 SP
|
|
238
|
+
- **Asignado:** @backend-dev
|
|
239
|
+
- **Estado:** ✅ Completada
|
|
240
|
+
- **Branch:** `docs/CHANGE-001-openapi`
|
|
241
|
+
|
|
242
|
+
**Subtareas:**
|
|
243
|
+
- [x] Especificación OpenAPI 3.0 de endpoints
|
|
244
|
+
- [x] Ejemplos de requests/responses
|
|
245
|
+
- [x] Códigos de error documentados
|
|
246
|
+
- [x] Integrar con Swagger UI
|
|
247
|
+
|
|
248
|
+
**Criterios de aceptación:**
|
|
249
|
+
- [x] OpenAPI spec válida
|
|
250
|
+
- [x] Swagger UI accesible en /api/docs
|
|
251
|
+
- [x] Ejemplos ejecutables
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
#### TASK-012: Actualizar README y guías
|
|
256
|
+
- **Ticket:** app-tienda-35
|
|
257
|
+
- **Estimación:** 1 SP
|
|
258
|
+
- **Asignado:** @tech-writer
|
|
259
|
+
- **Estado:** ✅ Completada
|
|
260
|
+
- **Branch:** `docs/CHANGE-001-readme`
|
|
261
|
+
|
|
262
|
+
**Subtareas:**
|
|
263
|
+
- [x] Actualizar README con setup de JWT
|
|
264
|
+
- [x] Documentar generación de claves RSA
|
|
265
|
+
- [x] Guía de uso de endpoints
|
|
266
|
+
- [x] Guía de troubleshooting
|
|
267
|
+
|
|
268
|
+
**Criterios de aceptación:**
|
|
269
|
+
- [x] README actualizado
|
|
270
|
+
- [x] Instrucciones claras para nuevos devs
|
|
271
|
+
- [x] Sección de seguridad documentada
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Dependencias entre Tareas
|
|
276
|
+
|
|
277
|
+
```mermaid
|
|
278
|
+
flowchart TD
|
|
279
|
+
T001["TASK-001<br/>Migración DB<br/>(1 SP)"] --> T006["TASK-006<br/>RateLimiting<br/>(2 SP)"]
|
|
280
|
+
T002["TASK-002<br/>Claves RSA<br/>(1 SP)"] --> T004["TASK-004<br/>JWTService<br/>(3 SP)"]
|
|
281
|
+
|
|
282
|
+
T001 --> T003["TASK-003<br/>UserRepository<br/>(2 SP)"]
|
|
283
|
+
T003 --> T005["TASK-005<br/>AuthService<br/>(3 SP)"]
|
|
284
|
+
T004 --> T005
|
|
285
|
+
|
|
286
|
+
T006 --> T007["TASK-007<br/>AuthController<br/>(3 SP)"]
|
|
287
|
+
T005 --> T007
|
|
288
|
+
|
|
289
|
+
T004 --> T008["TASK-008<br/>AuthMiddleware<br/>(2 SP)"]
|
|
290
|
+
|
|
291
|
+
T007 --> T009["TASK-009<br/>Unit Tests<br/>(2 SP)"]
|
|
292
|
+
T008 --> T009
|
|
293
|
+
|
|
294
|
+
T009 --> T010["TASK-010<br/>Integration Tests<br/>(2 SP)"]
|
|
295
|
+
|
|
296
|
+
T010 --> T011["TASK-011<br/>OpenAPI Docs<br/>(1 SP)"]
|
|
297
|
+
T010 --> T012["TASK-012<br/>README Update<br/>(1 SP)"]
|
|
298
|
+
|
|
299
|
+
style T001 fill:#e1f5ff
|
|
300
|
+
style T002 fill:#e1f5ff
|
|
301
|
+
style T003 fill:#fff4e1
|
|
302
|
+
style T004 fill:#fff4e1
|
|
303
|
+
style T005 fill:#fff4e1
|
|
304
|
+
style T006 fill:#ffe1e1
|
|
305
|
+
style T007 fill:#fff4e1
|
|
306
|
+
style T008 fill:#fff4e1
|
|
307
|
+
style T009 fill:#e1ffe1
|
|
308
|
+
style T010 fill:#e1ffe1
|
|
309
|
+
style T011 fill:#f0e1ff
|
|
310
|
+
style T012 fill:#f0e1ff
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Leyenda:**
|
|
314
|
+
- 🔵 Azul: Setup & Configuración
|
|
315
|
+
- 🟡 Amarillo: Backend/API
|
|
316
|
+
- 🔴 Rojo: Seguridad (Rate Limiting)
|
|
317
|
+
- 🟢 Verde: Testing
|
|
318
|
+
- 🟣 Morado: Documentación
|
|
319
|
+
|
|
320
|
+
## Testing Report
|
|
321
|
+
|
|
322
|
+
### Pre-merge Checklist
|
|
323
|
+
- [x] Todos los tests pasan
|
|
324
|
+
- [x] Cobertura mínima alcanzada (>80%)
|
|
325
|
+
- [x] Sin errores de linting
|
|
326
|
+
- [x] CodeRabbit review aprobado
|
|
327
|
+
- [x] PR review manual aprobado
|
|
328
|
+
|
|
329
|
+
### Resultados de Tests
|
|
330
|
+
|
|
331
|
+
| Suite | Tests | Passed | Failed | Coverage |
|
|
332
|
+
|-------|-------|--------|--------|----------|
|
|
333
|
+
| Unit | 45 | 45 | 0 | 91% |
|
|
334
|
+
| Integration | 12 | 12 | 0 | N/A |
|
|
335
|
+
| E2E | 3 | 3 | 0 | N/A |
|
|
336
|
+
|
|
337
|
+
**Total:** 60 tests, 60 passed, 0 failed
|
|
338
|
+
|
|
339
|
+
### Detalle de Cobertura por Componente
|
|
340
|
+
|
|
341
|
+
| Componente | Lines | Functions | Branches | Coverage |
|
|
342
|
+
|------------|-------|-----------|----------|----------|
|
|
343
|
+
| AuthService | 87/95 | 12/13 | 18/20 | 92% |
|
|
344
|
+
| JWTService | 114/120 | 8/8 | 22/23 | 95% |
|
|
345
|
+
| UserRepository | 42/48 | 4/5 | 10/11 | 88% |
|
|
346
|
+
| RateLimitMiddleware | 54/60 | 6/6 | 14/15 | 90% |
|
|
347
|
+
| AuthController | 98/110 | 10/12 | 20/24 | 89% |
|
|
348
|
+
| AuthMiddleware | 38/42 | 4/4 | 8/9 | 90% |
|
|
349
|
+
|
|
350
|
+
**Media total:** 91%
|
|
351
|
+
|
|
352
|
+
### CodeRabbit Report
|
|
353
|
+
|
|
354
|
+
- **PR:** #45 — `feat: Implementar autenticación JWT`
|
|
355
|
+
- **Status:** ✅ Aprobado
|
|
356
|
+
- **Issues encontrados:** 8
|
|
357
|
+
- **Issues resueltos:** 8
|
|
358
|
+
|
|
359
|
+
**Resumen de issues:**
|
|
360
|
+
1. ✅ Sugerencia: Extraer constantes mágicas (MAX_ATTEMPTS, LOCKOUT_TIME)
|
|
361
|
+
2. ✅ Seguridad: Añadir validación de longitud mínima de password
|
|
362
|
+
3. ✅ Performance: Cachear public key para validación de tokens
|
|
363
|
+
4. ✅ Documentación: Añadir docblocks faltantes
|
|
364
|
+
5. ✅ Testing: Añadir test de edge case (token con firma alterada)
|
|
365
|
+
6. ✅ Refactor: Simplificar lógica de rate limiting
|
|
366
|
+
7. ✅ Seguridad: Añadir sanitización de email antes de logging
|
|
367
|
+
8. ✅ Code style: Formatear según PSR-12
|
|
368
|
+
|
|
369
|
+
**Comentarios positivos:**
|
|
370
|
+
- "Excelente cobertura de tests"
|
|
371
|
+
- "Buena separación de responsabilidades"
|
|
372
|
+
- "Manejo robusto de errores"
|
|
373
|
+
|
|
374
|
+
### Security Scan (Semgrep)
|
|
375
|
+
|
|
376
|
+
- **Status:** ✅ Pasado
|
|
377
|
+
- **Vulnerabilidades críticas:** 0
|
|
378
|
+
- **Warnings:** 0
|
|
379
|
+
- **Fecha:** 2026-02-12 18:45 UTC
|
|
380
|
+
|
|
381
|
+
### Performance Tests
|
|
382
|
+
|
|
383
|
+
- **Endpoint:** POST /api/v1/auth/login
|
|
384
|
+
- **Carga:** 100 requests concurrentes
|
|
385
|
+
- **Resultado:**
|
|
386
|
+
- Tiempo medio: 245ms
|
|
387
|
+
- P95: 310ms
|
|
388
|
+
- P99: 420ms
|
|
389
|
+
- Tasa de éxito: 100%
|
|
390
|
+
|
|
391
|
+
✅ Cumple requisito de <500ms
|
|
392
|
+
|
|
393
|
+
## Notas de Implementación
|
|
394
|
+
|
|
395
|
+
### Decisiones Técnicas
|
|
396
|
+
|
|
397
|
+
1. **Bcrypt cost factor 12:** Balance entre seguridad y performance. Tiempo de hash ~250ms.
|
|
398
|
+
|
|
399
|
+
2. **RS256 para JWT:** Permite validación sin clave privada. Claves RSA 4096 bits.
|
|
400
|
+
|
|
401
|
+
3. **Rate limiting en DB:** Decisión temporal. En producción mover a Redis para mejor performance.
|
|
402
|
+
|
|
403
|
+
4. **Logging en DB:** Tabla `login_attempts` para auditoría. Considerar log rotation.
|
|
404
|
+
|
|
405
|
+
### Problemas Encontrados y Soluciones
|
|
406
|
+
|
|
407
|
+
#### Problema 1: Rate limiting lento con muchos intentos
|
|
408
|
+
**Causa:** Query de conteo en `login_attempts` sin límite de tiempo.
|
|
409
|
+
**Solución:** Añadir índice en `attempted_at` y filtrar por ventana de 15min.
|
|
410
|
+
**Commit:** a3f8d2e
|
|
411
|
+
|
|
412
|
+
#### Problema 2: Tokens grandes en cookies
|
|
413
|
+
**Causa:** JWT con claims innecesarios.
|
|
414
|
+
**Solución:** Minimizar payload (solo user_id, exp, iat).
|
|
415
|
+
**Commit:** b7e9c1a
|
|
416
|
+
|
|
417
|
+
#### Problema 3: Bloqueo de IPs compartidas (NAT corporativo)
|
|
418
|
+
**Causa:** Rate limiting solo por IP.
|
|
419
|
+
**Solución:** Combinar IP + email para rate limiting.
|
|
420
|
+
**Commit:** c4d8f3b
|
|
421
|
+
|
|
422
|
+
### Mejoras Futuras (fuera de alcance)
|
|
423
|
+
|
|
424
|
+
- [ ] Migrar rate limiting a Redis (app-tienda-40)
|
|
425
|
+
- [ ] Implementar 2FA (app-tienda-41)
|
|
426
|
+
- [ ] OAuth / Social login (app-tienda-42)
|
|
427
|
+
- [ ] Recuperación de contraseña (app-tienda-43)
|
|
428
|
+
- [ ] Rotación automática de claves RSA (app-tienda-44)
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
**Generado:** 2026-02-12
|
|
433
|
+
**Actualizado:** 2026-02-12 (PR merged)
|