📐

Plan Técnico — speckit-plan

Feature: Auth Redesign v2  ·  vion-core  ·  Generado 16 Jun 2026 · 10:05

Listo para implementar
Feature ID
FEAT-042
Sprint
Sprint 14
Estimación
18 puntos
Artefactos cargados
spec · constitution

Contexto y Objetivo

El sistema de autenticación actual (JWT stateless v1) no soporta revocación de tokens ni MFA. Auth Redesign v2 introduce refresh tokens rotativos, almacenamiento seguro en plataforma y soporte TOTP. La constitución del proyecto exige cero dependencias de terceros para el core de auth y cobertura de test ≥ 90%.

Decisiones de Diseño (ADR)

3 decisiones
ADR-01 · Refresh tokens rotativos con familia de revocación
Se adopta el patrón de rotación en cada uso. Si se detecta reutilización, toda la familia queda invalidada. Implementación en auth/token_family.py.
ADR-02 · Almacenamiento en Keychain (iOS) / EncryptedSharedPreferences (Android)
Ningún token se persiste en AsyncStorage ni localStorage. El wrapper multiplataforma encapsula la diferencia de API sin dependencias externas.
⚠ Impacto: requiere permiso USE_BIOMETRIC en AndroidManifest si se activa lock biométrico.
ADR-03 · TOTP con algoritmo RFC 6238 (propuesto, pendiente aprobación)
Biblioteca interna vion-totp sin dependencias npm. Ventana de tolerancia: ±1 intervalo de 30 s.

Pasos de Implementación

6 pasos
1
Migración de esquema de base de datos
Añadir tabla refresh_token_families con campos family_id, user_id, current_hash, revoked_at. Índices en user_id y family_id. Migration reversible.
db/migrations/0041_refresh_families.sql ~2h
2
Capa de token service
Implementar TokenService con métodos issue(), rotate() y revoke_family(). La rotación atomiza la invalidación del token anterior y emisión del nuevo en la misma transacción DB.
auth/token_family.py test_token_family.py ~4h
class TokenService:
    def rotate(self, refresh_token: str) -> TokenPair:
        # 1. Verificar token → obtener family
        family = self._verify_and_get_family(refresh_token)
        if family.is_revoked:
            # Reutilización detectada → revocar toda la familia
            self._revoke_family(family.id)
            raise TokenReplayError("Replay attack detected")
        # 2. Emitir nuevo par atómicamente
        return self._atomic_rotate(family)
3
Endpoints REST actualizados
Migrar POST /api/v2/auth/token y añadir POST /api/v2/auth/refresh y DELETE /api/v2/auth/revoke. Deprecar /api/v1/auth/* con header Deprecation: true.
api/v2/auth.py ~3h
4
Wrapper de almacenamiento seguro
Implementar SecureStorage con adaptadores para iOS Keychain, Android ESP y Web (sessionStorage con AES-256-GCM, clave en memoria). La API es idéntica entre plataformas.
storage/secure_storage.ts secure_storage.test.ts ~5h
5
Tests de integración E2E OAuth
Escenarios requeridos: happy path, token expirado → 401, replay attack → 401 + familia revocada, usuario deshabilitado → 403, MFA incorrecto → 401 con back-off.
tests/integration/test_auth_e2e.py ~3h
6
Feature flag y rollout gradual
Gate auth_v2_enabled en LaunchDarkly. Rollout: 5% → 25% → 100% con periodo de 2 semanas entre saltos. Métrica de guardia: tasa de errores de auth < 0.1%.
config/feature_flags.py ~1h

Análisis de Riesgos

Riesgo Impacto Mitigación
Condición de carrera en rotación concurrente Alto Transacción con SELECT FOR UPDATE en family_id
Desfase de reloj en validación TOTP Medio Ventana ±1 intervalo (90 s efectivos)
Clientes móviles cacheando tokens v1 Medio Header Cache-Control: no-store + versionado de respuesta
Degradación de latencia por consultas extra de familia Bajo Índice en family_id + Redis cache de familias activas