Decisión: Bump coordinado Cluster C auth/crypto — fido2 v2 + cryptography 47 + allauth 65.16
Contexto
El plan post-audit Fase B agrupó las dependencias de autenticación en Cluster C por su acoplamiento rígido: fido2 1.1.x imponía un cap cryptography<45 que bloqueaba bumps de seguridad de cryptography desde hacía meses. A su vez, django-allauth[mfa] 65.15.x solo soportaba fido2<1.2. Las tres debían subirse en un único commit coordinado o no podían subirse.
Este commit cierra el Cluster C como parte del plan post-audit Fase B (sesión 31). Tras él, solo queda el Cluster F (Redis/huey 3.0), diferido a junio 2026 por madurez de huey 3.0 (release 14-04-2026).
Decisión
Bump simultáneo de las tres librerías acopladas:
| Dependencia | Antes | Después | Notas |
|---|---|---|---|
cryptography | >=43.0.0,<45 | >=47.0.0,<49 | Cap <45 impuesto por fido2 1.1.x — liberado al subir fido2 a v2 |
fido2 | >=1.1.0,<1.2.0 | >=2.2.0,<3 | Salto mayor v1→v2 (breaking API changes) |
django-allauth[mfa] | 65.15.1 | 65.16.1 | Drop-in; acepta fido2<3,>=1.1.2 |
Impacto en código: core/auth_api.py::complete_biometric_setup
fido2 v2 introduce una API simplificada para el registro de passkeys. El único call site directo en el código (el resto del flujo WebAuthn lo gestiona allauth.mfa.webauthn) debía adaptarse.
Antes (fido2 v1)
from fido2.webauthn import (
AttestationObject,
CollectedClientData,
PublicKeyCredentialRpEntity,
)
def b64decode(s):
padding = 4 - len(s) % 4
if padding != 4:
s += "=" * padding
return base64.urlsafe_b64decode(s)
client_data = CollectedClientData(b64decode(credential["response"]["clientDataJSON"]))
attestation_object = AttestationObject(b64decode(credential["response"]["attestationObject"]))
server.register_complete(state, client_data, attestation_object)
Después (fido2 v2)
from fido2.webauthn import PublicKeyCredentialRpEntity
# v2: register_complete acepta el dict del cliente directamente (formato webauthn-json estándar).
# La librería deserializa internamente vía RegistrationResponse.from_dict().
server.register_complete(state, credential)
Eliminado: imports CollectedClientData, AttestationObject; helper local b64decode; deserialización manual de los dos blobs binarios (clientDataJSON, attestationObject).
Sin cambios: Fido2Server(rp, verify_origin=lambda) — firma estable en v2. PublicKeyCredentialRpEntity(id=..., name=...) — ya usaba kwargs (kwargs-only en v2 desde 2.0.0).
Alternativas consideradas
- No subir: inviable a largo plazo — el cap
cryptography<45bloquea CVEs de seguridad futuros. - Subir solo cryptography: imposible mientras fido2 1.1.x imponga
cryptography<45. - Subir fido2 sin adaptar auth_api.py: build roto —
CollectedClientDatayAttestationObjecteliminados de la API pública en v2. - Esperar a fido2 2.3+: innecesario; v2.2.0 es estable y allauth 65.16 la soporta explícitamente.
Riesgos y validación
El flujo de registro de passkeys desde login page es el único vector de riesgo real. El resto del ciclo passkey (login passwordless, TOTP coexistencia) lo gestiona allauth internamente y no tocó código custom.
Tests STAGE obligatorios antes de PROD (sin tests unitarios automatizables — WebAuthn requiere hardware autenticador real):
- Registro de passkey desde login page (
complete_biometric_setup) - Login passwordless con passkey registrada
- Coexistencia TOTP + passkey (misma cuenta)
- Logout vía POST
Lección aprendida
Los caps cruzados entre dependencias merecen revisión periódica. El cap
cryptography<45llevaba meses bloqueando bumps de seguridad, pero la causa raíz erafido2viejo. A veces la “imposibilidad” de bumpear A es porque B está desactualizado. Registrar caps con comentario de causa enrequirements.txt(como ya hace este repo) es clave para detectarlo.
Estado del plan post-audit Fase B
- Clusters cerrados: 10/10 + drift Regla 8 ✅
- Cluster C (auth/crypto): cerrado en esta sesión ✅
- Cluster F (Redis/huey 3.0): diferido a junio 2026 ⏳
Véase también
- [[entity—core—function—complete-biometric-setup]]
- [[concept—security—webauthn-passkeys]]
- [[decision—20260426—cluster-j2-python-nmap]]
- [[runbook—infra—stage-passkeys-validation]]
- [[concept—core—dependency-audit-fase-b]]