Skip to Content

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:

NavegadorVersiónAutenticador de PlataformaLlaves Itinerantes
Chrome67+Sí (Windows Hello, Touch ID)Sí (USB, NFC, BLE)
Safari14+Sí (Touch ID, Face ID)Sí
Firefox60+LimitadoSí (USB)
Edge18+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/browser

Implementación

Registrar una Passkey

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:

  1. El usuario completa la autenticación del primer factor
  2. Auris emite un token de sesión parcial (no un access token completo)
  3. Tu aplicación usa el token parcial para llamar al endpoint de desafío WebAuthn
  4. El usuario toca su llave de seguridad o escanea su biometría
  5. La aserción se verifica; Auris emite access y refresh tokens completos
POST/api/user/2fa/webauthn

Registra una nueva credencial WebAuthn para el usuario autenticado. Requiere un desafío de registro válido del endpoint de desafío.

POST/api/user/2fa/webauthn/challenge

Genera un desafío de registro o autenticación WebAuthn. Cuerpo: { type: 'registration' | 'authentication' }.

DELETE/api/user/2fa/webauthn/:credentialId

Elimina 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