LeadFlowAI — Deep Feature Repair
Módulo: src/features/auth/ · Stack: Next.js 14 + TypeScript + Prisma
6 tests fallando 5 fases Completado
Reparación Profunda — Módulo de Autenticación
LeadFlowAI SaaS · Protocolo focused-fix · 2026-06-12
Next.js 14 TypeScript Prisma 5 jose v5 Vitest
El módulo auth colapsó tras migrar de jsonwebtoken a jose v5. Login devuelve 500 en producción, el registro no emite JWT y el middleware rechaza tokens válidos. Se aplicó el protocolo estructurado de 5 fases para reparar sistemáticamente todo el módulo.
Tests antes 12/18
Tests después 18/18
Archivos cambiados 5
Fixes aplicados 7
Regresiones 0
1
SCOPE
6 archivos mapeados
2
TRACE
12 deps trazadas
3
DIAGNOSE
4 críticos, 3 warnings
4
FIX
7 fixes aplicados
5
VERIFY
18/18 tests OK
⚖️
LEY DE HIERRO: NO FIXES SIN COMPLETAR SCOPE → TRACE → DIAGNOSE PRIMERO
Si no has terminado la Fase 3, no puedes proponer soluciones. Sin excepciones.
1
SCOPE — Mapeo del Módulo
Identificar todos los archivos y puntos de entrada de la feature
6 archivos
FEATURE SCOPE: Primary path: src/features/auth/ Entry points: service.ts ← importado por 3 consumidores externos Internal files: repository.ts, types.ts, helpers.ts Config files: middleware.ts ← consume auth pero vive fuera de /auth/ Total files: 6 Total lines: 847
  • 🔑auth/service.tsPunto de entrada público — login, register, refresh
  • 🗄️auth/repository.tsAcceso a DB vía Prisma — getUserById, createUser
  • 📋auth/types.tsInterfaces TypeScript — UserResponse, JWTPayload, AuthResult
  • 🔧auth/helpers.tsUtilidades — hashPassword, comparePassword, generateRefreshToken
  • 🧪auth/__tests__/auth.test.ts18 tests Vitest — 6 fallando antes del fix
  • 🛡️src/middleware.tsMiddleware Next.js — protección de rutas (consumidor)
2
TRACE — Mapa de Dependencias
Dependencias entrantes (inbound) y salientes (outbound) completas
12 deps

⬇ Inbound — Esta feature depende de

jose v5 auth/service.ts · SignJWT, jwtVerify, JWTExpired
@prisma/client auth/repository.ts · PrismaClient, User
src/lib/db.ts auth/repository.ts · instancia Prisma singleton
bcryptjs auth/helpers.ts · hash, compare
process.env.JWT_SECRET auth/service.ts · firma y verificación
process.env.JWT_REFRESH_SECRET auth/service.ts · refresh tokens
process.env.DATABASE_URL auth/repository.ts · vía Prisma singleton
prisma/schema.prisma modelo User con campos id, email, passwordHash, role

⬆ Outbound — Consumidores de esta feature

api/login/route.ts importa { login } de auth/service
api/register/route.ts importa { register } de auth/service
api/refresh/route.ts importa { refreshAccessToken } de auth/service
src/middleware.ts importa { verifyToken } de auth/service
Env vars requeridas
Variable Estado
JWT_SECRET ✓ Presente
JWT_REFRESH_SECRET ✗ AUSENTE en .env.local
DATABASE_URL ✓ Presente
JWT_EXPIRES_IN ✓ Presente ("15m")
3
DIAGNOSE — Informe de Diagnóstico
4 críticos + 3 warnings encontrados antes de tocar código
4 críticos 3 warnings
HIGH auth/service.ts:23
API jose v5 incompatiblesignToken() usa la API de jsonwebtoken (sign(payload, secret, opts)). jose v5 requiere constructor new SignJWT(payload).setProtectedHeader().sign(encodedKey).
Causa raíz confirmada: La migración a jose v5 cambió la API pero el código nunca se actualizó. El import apunta a jose v5 pero los calls siguen la API de jsonwebtoken.
HIGH auth/service.ts:67
verifyToken retorna tipo incorrecto — Devuelve JWTVerifyResult de jose pero el middleware espera JWTPayload custom. Las props userId y role quedan undefined, causando que el middleware rechace todos los tokens.
Causa raíz confirmada: jose v5 envuelve el payload en result.payload. El código hacía return result en lugar de return result.payload.
HIGH auth/service.ts:112
JWT_REFRESH_SECRET no existe en .env.localrefreshAccessToken() llama process.env.JWT_REFRESH_SECRET que es undefined. jose lanza excepción al intentar codificar undefined como clave.
Causa raíz confirmada: La variable fue añadida al .env de producción pero nunca a .env.local, causando crash silencioso en desarrollo y producción (si no está seteada).
MED auth/types.ts:8
UserResponse no incluye campo role — El schema Prisma tiene role: String pero UserResponse no lo expone. Los consumidores castean a any para acceder al rol, rompiendo el tipado en 3 lugares.
Causa raíz confirmada: El campo fue añadido al schema en el último sprint pero el tipo TypeScript no se sincronizó.
MED auth/repository.ts:45
getUserByEmail sin manejo de error DB — No hay try/catch. Un fallo de conexión propaga una excepción Prisma sin capturar hasta el route handler.
LOW auth/helpers.ts:12
bcrypt rounds hardcodeado a 8 — Debería ser configurable vía env. 8 rounds es insuficiente según OWASP 2024 (mínimo 12).
LOW auth/__tests__/auth.test.ts:89
Mock de jose no actualizado — Los tests mockean la API antigua de jsonwebtoken. Los mocks deben reflejar la API de jose v5.

Estado de Tests Pre-Fix

✓ 12 pasando ✗ 6 fallando 18 total
Fallos: should sign JWT correctly · should verify valid token · should refresh access token · middleware should pass valid token · register should return token · token payload should include role
4
FIX — Reparación Sistemática
Orden: deps → tipos → lógica → tests → integración
7 fixes
1Agregar JWT_REFRESH_SECRET a .env.local
Archivo:.env.local Tipo:Fix de configuración (dependencias primero) Cambio:Añadido JWT_REFRESH_SECRET=leadflowai_refresh_secret_dev_only Test:✓ auth.test.ts:refreshToken → PASA
2Reescribir signToken() para jose v5 API
Archivo:auth/service.ts:23 Issue:API incompatible jsonwebtoken → jose v5
auth/service.ts — signToken()
- const token = sign(payload, secret, { expiresIn });
- return token;
+ const secretKey = new TextEncoder().encode(secret);
+ const token = await new SignJWT(payload)
+ .setProtectedHeader({ alg: 'HS256' })
+ .setIssuedAt()
+ .setExpirationTime(expiresIn)
+ .sign(secretKey);
+ return token;
Test:✓ should sign JWT correctly → PASA
3Corregir verifyToken() para extraer payload correctamente
Archivo:auth/service.ts:67 Issue:Retornaba JWTVerifyResult completo en lugar de result.payload
auth/service.ts — verifyToken()
- const result = await jwtVerify(token, secretKey);
- return result as JWTPayload;
+ const { payload } = await jwtVerify(token, secretKey);
+ return payload as JWTPayload;
Test:✓ should verify valid token → PASA · middleware should pass valid token → PASA
4Añadir campo role a UserResponse en types.ts
Archivo:auth/types.ts:8 Issue:Tipo desincronizado con schema Prisma
auth/types.ts — UserResponse
export interface UserResponse {
id: string;
email: string;
+ role: 'admin' | 'user' | 'viewer';
createdAt: Date;
}
Test:✓ token payload should include role → PASA
5Añadir try/catch a getUserByEmail en repository.ts
Archivo:auth/repository.ts:45 Issue:Excepción Prisma sin capturar en fallo de conexión Test:✓ repository error handling → PASA
6Subir bcrypt rounds de 8 a 12 (OWASP 2024)
Archivo:auth/helpers.ts:12 Issue:Rounds insuficientes, hardcodeados — ahora leen BCRYPT_ROUNDS del env (default 12) Test:✓ password hashing → PASA
7Actualizar mocks de Vitest para API jose v5
Archivo:auth/__tests__/auth.test.ts:89 Issue:vi.mock('jose') usaba API de jsonwebtoken Test:✓ Todos los mocks actualizados — 6/6 tests previamente fallidos → PASAN
5
VERIFY — Verificación Final
Tests del módulo + consumidores + suite completa
18/18 OK
🧪
18/18
Tests del módulo auth
🔌
12/12
Tests de consumidores
🎯
0
Regresiones detectadas
  • src/app/api/login/route.tslogin() resuelve correctamente con JWT válido
  • src/app/api/register/route.tsregister() crea usuario y emite token con role
  • src/app/api/refresh/route.tsrefreshAccessToken() funciona con JWT_REFRESH_SECRET
  • src/middleware.tsverifyToken() extrae payload correctamente — rutas protegidas OK
✓ FOCUSED FIX COMPLETE — Módulo auth operativo
5
Archivos modificados
7
Fixes aplicados
18/18
Tests passing
0
Regresiones
  • auth/service.ts— Reescritura completa de signToken() y verifyToken() para jose v5 API
  • auth/types.ts— Añadido campo role a UserResponse para sincronizar con schema Prisma
  • auth/repository.ts— Envuelto getUserByEmail en try/catch con error tipado
  • auth/helpers.ts— Bcrypt rounds 8→12, configurable vía BCRYPT_ROUNDS
  • auth/__tests__/auth.test.ts— Mocks actualizados a API jose v5
  • .env.local— Añadida variable JWT_REFRESH_SECRET faltante
CULTIVA IA · Skill: Reparación Profunda de Funcionalidades · Protocolo focused-fix v1.0 · eca0c128