Un sistema de demostración integral para FIDO2/CTAP2 que permite registrar, autenticar y gestionar credenciales utilizando llaves de seguridad como Google Titan, YubiKey y otros dispositivos FIDO2.
- Registro de Credenciales: Implementación completa de la ceremonia de registro WebAuthn
- Autenticación: Soporte para autenticación con y sin nombre de usuario (credenciales residentes/discoverable)
- Gestión de Credenciales: Visualización, eliminación y administración de credenciales almacenadas
- Soporte para Credenciales Residentes: Autenticación sin contraseña usando resident keys
- Verificación de Attestation: Verificación e información detallada de attestation
- FIDO MDS Integration: Reconocimiento automático de 274+ dispositivos certificados vía FIDO Alliance Metadata Service
- Base de Datos Persistente: H2 con persistencia en archivo - los datos sobreviven reinicios
- Testing Completo: 109+ tests unitarios e integración con 100% de cobertura en componentes críticos
- Detección Automática: Detección y conexión automática de dispositivos FIDO2
- Estado Visual: Indicadores visuales del estado del dispositivo (conectado, esperando toque, procesando, error)
- Información del Dispositivo: Visualización detallada de capacidades y características del dispositivo
- Reconocimiento de Dispositivos: Identificación automática mediante AAGUID con integración FIDO MDS
- Información de Certificación: Muestra estado de certificación FIDO de los dispositivos
- Reseteo de Dispositivos: Funcionalidad para resetear dispositivos FIDO2
- Notificaciones en Tiempo Real: WebSocket para actualizaciones de estado en tiempo real
- Descarga Automática: Descarga automática de metadata de https://mds3.fidoalliance.org/
- Cache Persistente: Almacenamiento local en
./data/mds-cache/para funcionamiento offline - Actualización Programada: Actualización automática diaria a las 2 AM
- 274+ Dispositivos: Base de datos oficial de dispositivos FIDO2 certificados
- Información Detallada: Descripción, fabricante, modelo, estado de certificación e iconos
- Fallback Automático: Si MDS no está disponible, usa lista estática de dispositivos conocidos
- Dashboard Interactivo: Panel de control con estadísticas y estado general
- Interfaz React Moderna: Frontend responsivo y fácil de usar
- Animaciones Visuales: Efectos visuales para indicar estado del dispositivo y acciones
- Multi-idioma: Preparado para múltiples idiomas (actualmente en inglés/español)
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Dashboard │ │Registration │ │Authentication│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────────────────────────────┐ │
│ │Credentials │ │ Device Manager │ │
│ └─────────────┘ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
│ HTTP/WebSocket
▼
┌─────────────────────────────────────────────────────────────┐
│ Backend (Spring Boot) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ WebAuthn │ │ Device │ │
│ │ Controller │ │ Controller │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ WebAuthn │ │ CTAP2 Device │ │
│ │ Service │ │ Manager │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Credential │ │ WebSocket │ │
│ │ Repository │ │ Config │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Dispositivos FIDO2/CTAP2 │
│ 🔑 Google Titan 🔐 YubiKey 🛡️ Otros FIDO2 │
└─────────────────────────────────────────────────────────────┘
- Java 21: Lenguaje de programación principal
- Spring Boot 3.2.1: Framework principal para el backend
- Spring WebSocket: Para comunicación en tiempo real con el frontend
- Spring Data JPA: Para persistencia de datos
- H2 Database: Base de datos en memoria para desarrollo
- Yubico WebAuthn Java: Biblioteca para implementación WebAuthn/FIDO2
- Maven: Gestión de dependencias y construcción
- React 18: Biblioteca principal para la interfaz de usuario
- Vite: Herramienta de construcción y desarrollo
- React Router: Navegación entre páginas
- SockJS + STOMP: WebSocket para comunicación en tiempo real
- Axios: Cliente HTTP para APIs REST
- Lucide React: Iconos modernos
- React Toastify: Notificaciones al usuario
- WebAuthn API: API estándar del navegador para FIDO2
- CTAP2 Protocol: Protocolo de comunicación con dispositivos FIDO2
- Attestation Verification: Verificación de autenticidad del dispositivo
- Resident Keys: Soporte para credenciales almacenadas en el dispositivo
fido2demo/
├── pom.xml # Configuración Maven principal
├── README.md # Este archivo
├── src/main/
│ ├── java/com/example/fido2demo/
│ │ ├── Fido2DemoApplication.java # Clase principal Spring Boot
│ │ ├── config/
│ │ │ └── WebSocketConfig.java # Configuración WebSocket
│ │ ├── controller/
│ │ │ ├── WebAuthnController.java # API REST para WebAuthn
│ │ │ └── DeviceController.java # API REST para dispositivos
│ │ ├── model/
│ │ │ ├── Credential.java # Modelo de credencial
│ │ │ └── DeviceInfo.java # Modelo de información del dispositivo
│ │ ├── repository/
│ │ │ └── CredentialRepository.java # Repositorio JPA
│ │ └── service/
│ │ ├── WebAuthnService.java # Lógica de negocio WebAuthn
│ │ └── Ctap2DeviceManager.java # Gestión de dispositivos CTAP2
│ └── resources/
│ └── application.properties # Configuración de la aplicación
├── frontend/ # Aplicación React
│ ├── package.json # Dependencias Node.js
│ ├── vite.config.js # Configuración Vite
│ ├── index.html # HTML principal
│ └── src/
│ ├── main.jsx # Punto de entrada React
│ ├── App.jsx # Componente principal
│ ├── index.css # Estilos globales
│ ├── components/ # Componentes React
│ │ ├── Dashboard.jsx # Panel principal
│ │ ├── Registration.jsx # Registro de credenciales
│ │ ├── Authentication.jsx # Autenticación
│ │ ├── Credentials.jsx # Gestión de credenciales
│ │ └── DeviceManager.jsx # Gestión de dispositivos
│ └── context/
│ └── DeviceContext.jsx # Context para estado de dispositivos
└── target/ # Archivos compilados (generado)
-
Java 21 o superior
java -version
-
Maven 3.8 o superior
mvn -version
-
Node.js 18 o superior y npm
node --version npm --version
-
Navegador Web Moderno con soporte para WebAuthn (Chrome 70+, Firefox 60+, Safari 14+)
-
Dispositivo FIDO2 (Google Titan, YubiKey, etc.) - Opcional, el sistema incluye dispositivos simulados
-
Clonar el repositorio
git clone <repository-url> cd fido2demo
-
Compilar y ejecutar la aplicación
mvn clean spring-boot:run
Este comando:
- Descarga todas las dependencias Java
- Instala Node.js y npm automáticamente
- Compila el frontend React
- Compila el backend Java
- Inicia la aplicación en el puerto 8081
-
Acceder a la aplicación
- Abrir navegador en: http://localhost:8081
- La aplicación incluye dispositivos FIDO2 simulados para demostración
- Estadísticas: Visualización de dispositivos conectados, credenciales registradas y usuarios
- Estado de Dispositivos: Información en tiempo real sobre dispositivos FIDO2 conectados
- Accesos Rápidos: Enlaces directos a las principales funcionalidades
- Navegar a Register en el menú
- Introducir username y display name
- Seleccionar si se requiere resident key (credencial discoverable)
- Hacer clic en Register Credential
- Tocar el dispositivo FIDO2 cuando se solicite
- Navegar a Authenticate en el menú
- Opción A - Con username: Introducir nombre de usuario
- Opción B - Sin username: Dejar vacío para usar credenciales discoverable
- Seleccionar User Verification si se requiere PIN/biometría
- Hacer clic en Authenticate
- Tocar el dispositivo FIDO2 cuando se solicite
- Navegar a Credentials en el menú
- Ver todas las credenciales registradas
- Filtrar por tipo (todas, discoverable, regulares)
- Buscar por usuario o ID de credencial
- Ver detalles completos de cada credencial
- Eliminar credenciales individuales o todas las de un usuario
- Navegar a Devices en el menú
- Ver dispositivos conectados y desconectados
- Información detallada del dispositivo (firmware, capacidades, etc.)
- Escanear nuevos dispositivos
- Resetear dispositivos (elimina todas las credenciales)
- Actualizar información del dispositivo
POST /api/webauthn/register/begin # Iniciar registro
POST /api/webauthn/register/finish # Completar registro
POST /api/webauthn/authenticate/begin # Iniciar autenticación
POST /api/webauthn/authenticate/finish # Completar autenticación
GET /api/webauthn/credentials # Listar credenciales
GET /api/webauthn/credentials/user/{user} # Credenciales por usuario
GET /api/webauthn/credentials/discoverable # Credenciales discoverable
DEL /api/webauthn/credentials/{id} # Eliminar credencial
DEL /api/webauthn/credentials/user/{user} # Eliminar todas del usuario
GET /api/devices # Listar dispositivos conectados
GET /api/devices/{id} # Información de dispositivo específico
POST /api/devices/scan # Escanear nuevos dispositivos
POST /api/devices/{id}/reset # Resetear dispositivo
POST /api/devices/{id}/info # Actualizar información del dispositivo
/ws # Conexión WebSocket
/topic/devices/connected # Dispositivo conectado
/topic/devices/disconnected # Dispositivo desconectado
/topic/devices/status # Cambio de estado del dispositivo
- ✅ Registration Ceremony: Registro completo con attestation
- ✅ Authentication Ceremony: Autenticación con y sin allowCredentials
- ✅ User Verification: Soporte para PIN y verificación biométrica
- ✅ User Presence: Detección de toque del usuario
- ✅ Platform Credentials: Credenciales del dispositivo
- ✅ Cross-Platform Credentials: Credenciales de llaves externas
- ✅ Resident Keys: Credenciales almacenadas en el dispositivo
- ✅ Non-Resident Keys: Credenciales con allowCredentials
- ✅ Attestation Object: Procesamiento y almacenamiento
- ✅ AAGUID: Identificación del modelo del dispositivo
- ✅ Attestation Verification: Verificación básica de attestation
- ✅ credProps: Implementado - Confirma el estado de resident key (RK) del authenticator
- 🔄 largeBlob: Planificado
- 🔄 credentialProtectionPolicy: Planificado
La extensión credProps permite confirmar si una credencial fue realmente creada como discoverable (resident key) por el authenticator. Esto es útil porque algunos authenticators pueden no soportar resident keys incluso cuando se solicitan.
Funcionalidad:
- Se solicita automáticamente durante el registro
- El authenticator confirma si la credencial es discoverable (RK=true/false)
- El resultado se almacena en la base de datos para referencia futura
- Permite detectar discrepancias entre lo solicitado y lo implementado
Implementación:
- Backend solicita la extensión en
WebAuthnService.startRegistration()(línea 85-87) - Backend procesa la respuesta en
WebAuthnService.saveCredential()(línea 201-209) - El valor confirmado se guarda en
Credential.discoverable
- Origin Validation: Validación estricta del origen de las requests
- Challenge Validation: Verificación de challenge únicos y temporales
- Signature Verification: Verificación criptográfica de signatures
- Counter Verification: Validación de signature counter para replay protection
- HTTPS Only: Configurado para requerir HTTPS en producción
- Almacenamiento seguro de claves públicas
- Gestión adecuada de sesiones temporales
- Validación de entrada en todos los endpoints
- Logging de eventos de seguridad
- Timeouts apropiados para ceremonias
El sistema incluye dispositivos FIDO2 simulados para desarrollo:
- Google Titan Security Key: Simulación completa con AAGUID real
- YubiKey 5 Series: Se puede activar escaneando dispositivos
- Estados simulados: conectado, esperando toque, procesando, error
- Conectar dispositivo FIDO2 físico
- El sistema detectará automáticamente dispositivos compatibles
- Usar funcionalidad "Scan for Devices" para actualizar la lista
- Realizar ceremonias de registro y autenticación reales
- H2 Console: http://localhost:8081/h2-console (usuario: sa, sin contraseña)
- Logs de Aplicación: Configurados en
application.properties - WebSocket Debug: Mensajes de debug en la consola del navegador
- Network Tab: Inspección de requests/responses WebAuthn
- Base de Datos: Cambiar de H2 a PostgreSQL/MySQL
- HTTPS: Configurar certificados SSL/TLS
- Origin Configuration: Actualizar origins permitidos
- Logging: Configurar logging para producción
- Security Headers: Añadir headers de seguridad adicionales
# Base de datos
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/fido2demo
SPRING_DATASOURCE_USERNAME=fido2user
SPRING_DATASOURCE_PASSWORD=secure_password
# FIDO2 Configuration
FIDO2_RELYING_PARTY_ID=your-domain.com
FIDO2_RELYING_PARTY_ORIGINS=https://your-domain.com
# Servidor
SERVER_PORT=8080
SERVER_SSL_ENABLED=trueFROM openjdk:21-jdk-slim
COPY target/fido2demo-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]- Actualizar
Ctap2DeviceManager.javacon nuevos AAGUIDs - Añadir iconos específicos en
DeviceManager.jsx - Configurar capacidades específicas del dispositivo
- Modificar estilos en
frontend/src/index.css - Actualizar componentes React en
frontend/src/components/ - Añadir nuevos idiomas en el sistema de internacionalización
- Implementar extensiones en
WebAuthnService.java - Actualizar frontend para manejar nuevas extensiones
- Añadir validación y procesamiento específico
- Número de credenciales registradas
- Dispositivos conectados/desconectados
- Intentos de autenticación exitosos/fallidos
- Tiempos de respuesta de ceremonias
- Eventos de registro y autenticación
- Conexión/desconexión de dispositivos
- Errores y excepciones detalladas
- Métricas de rendimiento
- Fork del repositorio
- Crear rama para nueva funcionalidad
- Implementar cambios con tests
- Crear Pull Request con descripción detallada
- Soporte para extensiones WebAuthn adicionales
- Integración con dispositivos FIDO2 reales vía HID
- Interfaz de administración avanzada
- Soporte para múltiples Relying Parties
- API REST completa para integración externa
- Tests automatizados E2E
- Documentación interactiva con Swagger
- Documentación FIDO Alliance: https://fidoalliance.org/specifications/
- WebAuthn Specification: https://www.w3.org/TR/webauthn-2/
- Yubico WebAuthn Guide: https://developers.yubico.com/WebAuthn/
Para reportar problemas o solicitar funcionalidades, crear un issue en el repositorio con:
- Descripción detallada del problema
- Pasos para reproducir
- Información del navegador y dispositivo FIDO2
- Logs relevantes
Este proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para detalles.
Durante el desarrollo de este proyecto, se encontraron y resolvieron varios problemas técnicos críticos relacionados con la implementación de WebAuthn/FIDO2. Esta sección documenta estos problemas y sus soluciones para ayudar a otros desarrolladores.
Problema: Los ArrayBuffers de WebAuthn necesitan codificarse como Base64url (variante URL-safe de Base64) al enviarse al backend, no como Base64 estándar.
Síntomas:
- Error CBOR: "Unexpected data encountered" al parsear credentials
- Fallo en
PublicKeyCredential.parseRegistrationResponseJson()oparseAssertionResponseJson() - Datos corruptos al intentar decodificar en el backend
Causa Raíz: Base64url usa - y _ en lugar de + y /, y no incluye padding =. Si se envía Base64 estándar, el backend no puede decodificar correctamente.
Solución Implementada:
// Frontend - Conversión correcta ArrayBuffer → Base64url
const arrayBufferToBase64url = (buffer) => {
const bytes = new Uint8Array(buffer)
let binary = ''
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i])
}
// Convertir a base64 y luego a base64url
return btoa(binary)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '') // Eliminar padding
}
// Frontend - Conversión correcta Base64url → ArrayBuffer
const base64UrlToArrayBuffer = (base64url) => {
// Convertir base64url a base64
const base64 = base64url.replace(/-/g, '+').replace(/_/g, '/')
// Añadir padding si es necesario
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, '=')
const binary = atob(padded)
const buffer = new ArrayBuffer(binary.length)
const view = new Uint8Array(buffer)
for (let i = 0; i < binary.length; i++) {
view[i] = binary.charCodeAt(i)
}
return buffer
}Archivos Afectados:
frontend/src/components/Registration.jsx: líneas 147-174frontend/src/components/Authentication.jsx: líneas 172-195
Problema: Cada registro generaba un userHandle aleatorio diferente, causando que el mismo username tuviera diferentes userHandles en registros sucesivos.
Síntomas:
- Error: "Unknown credential: ByteArray(...)" durante autenticación
- Credentials no encontrados aunque existían en la base de datos
CredentialRepository.lookup()fallaba al no coincidir userHandles
Causa Raíz: El método generateUserHandle() generaba bytes aleatorios en cada llamada:
// INCORRECTO - Genera valores diferentes cada vez
private byte[] generateUserHandle(String username) {
byte[] userHandle = new byte[32];
random.nextBytes(userHandle);
return userHandle;
}Solución Implementada:
// CORRECTO - Genera el mismo userHandle para el mismo username
private byte[] generateUserHandle(String username) {
// Generate a consistent userHandle based on username
// This ensures the same username always gets the same userHandle
return (username + "-user-handle").getBytes(StandardCharsets.UTF_8);
}Explicación: El userHandle debe ser consistente para un mismo usuario a través de múltiples registros y autenticaciones. El WebAuthn Relying Party usa este valor para buscar credentials asociadas.
Archivos Afectados:
src/main/java/com/example/fido2demo/service/WebAuthnService.java: líneas 271-275
Problema: Las credenciales marcadas como "Require Resident Key" se guardaban en la base de datos con discoverable=false, impidiendo la autenticación passwordless sin username.
Síntomas:
- Autenticación sin username fallaba incluso con resident keys registradas
- Campo
discoverablesiempre false en la base de datos - No se podían listar credenciales discoverable
Causa Raíz: El flag discoverable estaba hardcoded a false:
// INCORRECTO
dbCredential.setDiscoverable(false); // Siempre falseSolución Implementada:
// CORRECTO - Detecta del PublicKeyCredentialCreationOptions
boolean isDiscoverable = request.getAuthenticatorSelection()
.map(authSel -> authSel.getResidentKey()
.map(rk -> rk == ResidentKeyRequirement.REQUIRED)
.orElse(false))
.orElse(false);
dbCredential.setDiscoverable(isDiscoverable);Cómo Funciona la Autenticación Passwordless:
-
Registro con Resident Key:
- Usuario marca checkbox "Require Resident Key"
- Backend configura
residentKey: REQUIREDenAuthenticatorSelectionCriteria - Credential se almacena en el dispositivo FIDO2
- Base de datos marca
discoverable=true
-
Autenticación sin Username:
- Usuario deja campo username vacío
- Backend NO incluye
allowCredentialsen la request - Navegador/dispositivo presenta todas las resident keys para el dominio
- Usuario selecciona una credential del dispositivo
- Backend busca credential por
credentialIdy valida signature
Archivos Afectados:
src/main/java/com/example/fido2demo/service/WebAuthnService.java: líneas 150-156
Problema: El credential JSON se estaba serializando dos veces, causando que el backend recibiera una string JSON dentro de otra string JSON.
Síntomas:
- Error: "Unexpected data encountered" en el backend
- JSON con escapes:
"{\"id\":\"...\"}"en lugar de{"id":"..."} - Fallo al parsear con
PublicKeyCredential.parseRegistrationResponseJson()
Causa Raíz:
// INCORRECTO - Double stringify
const credentialData = {
id: credential.id,
// ...
}
body: JSON.stringify({
sessionId,
credential: JSON.stringify(credentialData) // Ya dentro de JSON.stringify()
})Solución Implementada:
// CORRECTO - Single stringify en variable, luego usar como string
const credentialJson = JSON.stringify({
id: credential.id,
rawId: arrayBufferToBase64url(credential.rawId),
type: credential.type,
response: { /* ... */ }
})
const response = await fetch('/api/webauthn/register/finish', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId,
credential: credentialJson // String, no objeto
})
})Archivos Afectados:
frontend/src/components/Registration.jsx: líneas 103-124frontend/src/components/Authentication.jsx: líneas 132-155
Problema: El controller generaba un sessionId diferente al que el service almacenaba, causando que las requests de finish fallaran.
Síntomas:
- Error: "No pending registration/authentication found for session"
- SessionId enviado por frontend no encontrado en
pendingRegistrations/pendingAuthentications
Causa Raíz:
// INCORRECTO - Controller genera su propio sessionId
@PostMapping("/authenticate/begin")
public ResponseEntity<?> beginAuthentication(@RequestBody AuthenticationRequest request) {
AssertionRequest assertionRequest = webAuthnService.startAuthentication(...);
String sessionId = generateSessionId(); // Diferente al del service!
// ...
}Solución Implementada:
// CORRECTO - Service devuelve el sessionId que generó
public class AuthenticationStartResponse {
private final AssertionRequest assertionRequest;
private final String sessionId; // El mismo que se guarda en pendingAuthentications
// ...
}
@PostMapping("/authenticate/begin")
public ResponseEntity<?> beginAuthentication(@RequestBody AuthenticationRequest request) {
AuthenticationStartResponse response = webAuthnService.startAuthentication(...);
return ResponseEntity.ok(Map.of(
"publicKey", response.getAssertionRequest().getPublicKeyCredentialRequestOptions(),
"sessionId", response.getSessionId() // Usar el del service
));
}Archivos Afectados:
src/main/java/com/example/fido2demo/service/WebAuthnService.java: líneas 162-173, 175-199src/main/java/com/example/fido2demo/controller/WebAuthnController.java: líneas 76-97
Problema: El campo transports venía como null o tipo incorrecto desde el backend, causando errores de validación en el navegador.
Síntomas:
- Error: "Failed to read the 'transports' property from 'PublicKeyCredentialDescriptor': The provided value cannot be converted to a sequence"
- Fallo al llamar
navigator.credentials.create()oget()
Solución Implementada:
// Limpiar transports inválidos
allowCredentials: publicKey.allowCredentials?.map(cred => {
const converted = {
...cred,
id: base64UrlToArrayBuffer(cred.id)
}
// Eliminar transports si es null o no es array
if (!converted.transports || !Array.isArray(converted.transports)) {
delete converted.transports
}
return converted
}) || []Archivos Afectados:
frontend/src/components/Registration.jsx: líneas 184-194frontend/src/components/Authentication.jsx: líneas 65-76
Problema: El backend devolvía extensiones con valores null, causando que el navegador rechazara la request.
Síntomas:
- Errores de validación en
navigator.credentials.create() - Extensions con valores undefined/null
Solución Implementada:
// Limpiar extensions antes de enviar al WebAuthn API
if (convertedPublicKey.extensions) {
Object.keys(convertedPublicKey.extensions).forEach(key => {
if (convertedPublicKey.extensions[key] === null ||
convertedPublicKey.extensions[key] === undefined) {
delete convertedPublicKey.extensions[key]
}
})
// Eliminar objeto extensions si está vacío
if (Object.keys(convertedPublicKey.extensions).length === 0) {
delete convertedPublicKey.extensions
}
}Archivos Afectados:
frontend/src/components/Registration.jsx: líneas 206-218frontend/src/components/Authentication.jsx: líneas 86-96
- Base64url vs Base64: Siempre usar Base64url para datos WebAuthn, nunca Base64 estándar
- Consistencia de UserHandle: El userHandle debe ser determinístico basado en el username
- Single Source of Truth: Los IDs de sesión deben generarse en un solo lugar (service layer)
- Validación de Datos: Limpiar valores null/undefined antes de enviar al WebAuthn API
- Resident Keys: El flag discoverable debe detectarse del AuthenticatorSelection original
- Serialización JSON: Cuidado con double-stringify al enviar credentials al backend
- Logging: Añadir logs detallados ayuda enormemente en debugging de WebAuthn
El proyecto funciona perfectamente con Chrome DevTools Virtual Authenticator:
- Abrir Chrome DevTools (F12)
- Ir a "More tools" → "WebAuthn"
- Click en "Enable virtual authenticator environment"
- Add authenticator con las opciones deseadas:
- Protocol: ctap2
- Transport: usb
- Supports resident keys: ✓
- Supports user verification: ✓
Todos los flujos (registro con/sin resident key, autenticación con/sin username) funcionan correctamente.
¡Gracias por usar FIDO2 Demo! 🔐✨
Este sistema de demostración está diseñado para ser una implementación de referencia completa de FIDO2/WebAuthn, proporcionando tanto un ejemplo funcional como una base sólida para implementaciones en producción.