Passkeys / WebAuthn
WebAuthn (Web Authentication, parte del estándar FIDO2) permite la autenticación sin contraseña y de segundo factor usando biometría del dispositivo (Touch ID, Face ID, Windows Hello), autenticadores de plataforma integrados en el sistema operativo, o llaves de seguridad de hardware itinerantes (YubiKey, Titan Key).
Auris implementa la API WebAuthn tanto como método de autenticación sin contraseña independiente como segundo factor 2FA tras el inicio de sesión con correo/contraseña.
Compatibilidad del Navegador
WebAuthn es compatible con todos los navegadores modernos:
| Navegador | Versión | Autenticador de Plataforma | Llaves Itinerantes |
|---|---|---|---|
| Chrome | 67+ | Sí (Windows Hello, Touch ID) | Sí (USB, NFC, BLE) |
| Safari | 14+ | Sí (Touch ID, Face ID) | Sí |
| Firefox | 60+ | Limitado | Sí (USB) |
| Edge | 18+ | Sí (Windows Hello) | Sí |
| Safari (iOS) | 14+ | Sí (Face ID, Touch ID) | Sí |
| Chrome (Android) | 70+ | Sí (huella dactilar) | Sí (NFC) |
Las passkeys (credenciales sincronizadas a través de iCloud Keychain, Google Password Manager o 1Password) son una forma de WebAuthn. Auris admite el registro y la autenticación de passkeys cuando el autenticador del dispositivo admite el requisito residentKey.
Cómo Funciona
Flujo de Registro
El servidor genera un desafío de registro
Auris genera un desafío criptográficamente aleatorio y lo devuelve junto con las opciones de registro (información del relying party, información del usuario, tipos de credenciales permitidos, criterios de selección del autenticador).
El navegador crea una credencial
El navegador llama a navigator.credentials.create() con las opciones de registro. El autenticador de plataforma (o llave de seguridad) solicita al usuario confirmación biométrica o PIN, genera un par de claves pública/privada y devuelve la clave pública y un objeto de atestación.
El servidor almacena la credencial
Tu aplicación envía los datos de la credencial a Auris. Auris verifica la atestación, extrae la clave pública y almacena el ID de credencial y la clave pública en la base de datos asociada a la cuenta del usuario.
Flujo de Autenticación
El servidor genera un desafío de autenticación
Auris genera un nuevo desafío y devuelve las opciones de autenticación incluyendo la lista de IDs de credenciales registradas para el usuario (o vacía para el flujo de clave residente/descubrible).
El navegador realiza una aserción
El navegador llama a navigator.credentials.get(). El autenticador encuentra una credencial coincidente, firma el desafío con la clave privada y devuelve la aserción.
El servidor verifica la aserción
Auris verifica la firma de la aserción usando la clave pública almacenada. Si es válida, la autenticación se completa y se emiten los tokens.
Biblioteca del Lado del Cliente
Auris usa @simplewebauthn/browser en el lado del cliente para gestionar las llamadas a la API WebAuthn del navegador. Esta biblioteca abstrae las incompatibilidades entre navegadores y proporciona envoltorios tipados para navigator.credentials.create() y navigator.credentials.get().
npm install @simplewebauthn/browserImplementación
Registrar una Passkey
JavaScript
import { startRegistration } from '@simplewebauthn/browser'
async function registrarPasskey(accessToken) {
// Paso 1: Obtener opciones de registro de Auris
const respuestaOpciones = await fetch('/api/user/2fa/webauthn/challenge', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ type: 'registration' }),
})
const opciones = await respuestaOpciones.json()
// Paso 2: El navegador solicita al usuario biometría/llave de seguridad
let credencial
try {
credencial = await startRegistration(opciones)
} catch (err) {
if (err.name === 'NotAllowedError') {
console.error('Usuario canceló o tiempo de espera agotado')
return
}
throw err
}
// Paso 3: Enviar credencial a Auris
const respuestaVerif = await fetch('/api/user/2fa/webauthn', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
credential: credencial,
name: 'Mi MacBook', // Nombre proporcionado por el usuario para la credencial
}),
})
const resultado = await respuestaVerif.json()
if (resultado.verified) {
console.log('Passkey registrada con éxito')
}
}Autenticarse con una Passkey
import { startAuthentication } from '@simplewebauthn/browser'
async function autenticarConPasskey() {
// Paso 1: Obtener opciones de autenticación de Auris
const respuestaOpciones = await fetch('/api/auth/webauthn/challenge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
const opciones = await respuestaOpciones.json()
// Paso 2: El navegador solicita al usuario (biometría o toque de llave)
let asercion
try {
asercion = await startAuthentication(opciones)
} catch (err) {
if (err.name === 'NotAllowedError') {
console.error('Usuario canceló o no se encontró credencial coincidente')
return
}
throw err
}
// Paso 3: Verificar aserción y recibir tokens
const respuestaVerif = await fetch('/api/auth/webauthn/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ assertion: asercion }),
})
const resultado = await respuestaVerif.json()
if (resultado.accessToken) {
// Almacenar tokens y redirigir
console.log('Autenticado como:', resultado.user)
}
}WebAuthn como 2FA
Cuando se configura como segundo factor (en lugar de método sin contraseña independiente), WebAuthn se usa después de que se complete el primer factor (correo/contraseña o social).
Configuración: Habilita WebAuthn 2FA en Consola → Autenticación → Autenticación Multifactor → Passkeys.
Flujo 2FA:
- El usuario completa la autenticación del primer factor
- Auris emite un token de sesión parcial (no un access token completo)
- Tu aplicación usa el token parcial para llamar al endpoint de desafío WebAuthn
- El usuario toca su llave de seguridad o escanea su biometría
- La aserción se verifica; Auris emite access y refresh tokens completos
/api/user/2fa/webauthnRegistra una nueva credencial WebAuthn para el usuario autenticado. Requiere un desafío de registro válido del endpoint de desafío.
/api/user/2fa/webauthn/challengeGenera un desafío de registro o autenticación WebAuthn. Cuerpo: { type: 'registration' | 'authentication' }.
/api/user/2fa/webauthn/:credentialIdElimina una credencial WebAuthn registrada por su ID.
Gestión de Credenciales
Los usuarios pueden registrar múltiples credenciales — una por dispositivo o llave de seguridad. A cada credencial se le puede dar un nombre descriptivo para identificarla.
La mejor práctica es permitir que los usuarios registren al menos dos credenciales (ej. Touch ID del portátil y una llave de hardware) para tener una copia de seguridad si se pierde un dispositivo.
Auris almacena por credencial:
- ID de credencial (codificado en base64url, identificador único)
- Clave pública (formato COSE)
- Contador de firmas (incrementado en cada uso; Auris detecta clonación de autenticador si el contador regresa)
- Nombre proporcionado por el usuario
- Marca de tiempo del último uso
- Marca de tiempo de creación
- Tipo de vinculación del autenticador (plataforma o multiplataforma)
Consideraciones de Seguridad
Resistencia al phishing — Las credenciales WebAuthn están vinculadas al origen del relying party (rpId, que es tu dominio). Un sitio de phishing en un dominio diferente no puede usar credenciales emitidas para tu dominio. Esta es una ventaja significativa sobre las contraseñas y los códigos OTP.
Sin secretos compartidos — La clave privada nunca abandona el autenticador. Auris almacena únicamente la clave pública. Una brecha en la base de datos no expone ningún material de credencial.
Validación del contador de firmas — Auris rastrea el contador de firma interno del autenticador. Si llega una aserción con un contador igual o menor al valor almacenado, Auris marca la credencial como potencialmente clonada y requiere revisión.
Claves residentes y credenciales descubribles — Cuando se establece residentKey: required, la credencial se almacena en el propio autenticador y puede usarse sin proporcionar primero un nombre de usuario (inicio de sesión sin nombre de usuario). Esta es la base de los flujos modernos de passkeys.
Para máxima seguridad, combina WebAuthn con un autenticador de plataforma (integrado en el dispositivo) en lugar de solo una llave de seguridad itinerante. Los autenticadores de plataforma requieren desbloqueo del dispositivo (biometría o PIN), añadiendo un factor de posesión + conocimiento sin fricción para el usuario.
Guías Relacionadas
- SMS OTP — Segundo factor basado en teléfono
- Hosted Login (PKCE) — Flujo de autenticación interactivo estándar
- Magic Links — Autenticación sin contraseña basada en correo electrónico