Las contraseñas son una de esas cosas que hemos normalizado simplemente porque hemos vivido con ellas durante años. Los usuarios los olvidan, los reutilizan, los escriben donde no deberían. Los equipos deben gestionar restablecimientos, políticas, hashes, filtraciones, phishing y soporte.
El passkey no hace que la autenticación sea perfecta, pero elimina un gran problema: el servidor ya no tiene que guardar un secreto compartido con el usuario.
¿Qué pasa realmente?
Un passkey es una credencial basada en WebAuthn. Cuando el usuario lo crea, el dispositivo genera un par de claves:
- una clave privada, que permanece en el dispositivo o en el administrador de contraseñas;
- una clave pública, que el servidor puede guardar.
Al iniciar sesión, el servidor no pregunta "dime la contraseña". Envía un desafío aleatorio. El dispositivo lo firma con la clave privada. El servidor verifica la firma con la clave pública.
Esa es la parte buena: si roban la base de datos, no hay contraseñas en su interior que puedan descifrarse. Y si un usuario termina en un dominio falso, el passkey no es válido para ese dominio. No se trata sólo de comodidad, sino de una protección concreta contra el phishing.
El navegador actúa como puente
En el navegador los dos principales API son:
navigator.credentials.create()para crear un passkey;navigator.credentials.get()para usarlo durante el inicio de sesión.
Pero la lógica importante está en el servidor. El servidor debe generar el desafío, guardarlo temporalmente, verificar la respuesta, verificar la fuente y Relying Party ID, luego crear la sesión.
La parte del cliente debería ser casi aburrida:
const options = await fetch('/api/passkeys/login/options').then((r) => r.json()); const credential = await navigator.credentials.get({ publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options), }); await fetch('/api/passkeys/login/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credential?.toJSON()), });
Si te encuentras implementando el cifrado WebAuthn manualmente, detente. Utilice una biblioteca sólida del lado del servidor. Los errores aquí no son "errores lindos", son agujeros de autenticación.
Qué guardar en la base de datos
No hay necesidad de salvar a la mitad del mundo. Generalmente es suficiente:
- ID de la credencial;
- clave pública;
- usuario conectado;
- cualquier contador de verificación o metadatos;
- transportes, si son útiles para la UX;
- nombre elegido por el usuario;
- fecha de creación y último uso.
Una tabla mínima podría verse así:
create table passkey_credentials ( id uuid primary key default gen_random_uuid(), user_id uuid not null references users(id), credential_id text not null unique, public_key text not null, name text, created_at timestamptz not null default now(), last_used_at timestamptz );
Luego agregaría registros de auditoría y notificaciones: si alguien crea un nuevo passkey en mi cuenta, quiero saberlo.
UX importa más que la demostración
La demostración de un passkey siempre es hermosa: haces clic en Face ID y estás dentro. El producto real es más complicado.
Alguien cambia su teléfono. Alguien utiliza una computadora de la empresa bloqueada. Algunas personas no entienden por qué el navegador sugiere un passkey. Alguien pierde el acceso a su dispositivo.
Por eso no empezaría con "a partir de hoy no más contraseñas para todos". Yo empezaría así:
- passkey opcional para usuarios internos;
- sugerencia de crear uno después de iniciar sesión correctamente;
- página de cuenta para cambiar el nombre y eliminar passkey;
- claro respaldo;
- Implementación gradual en el inicio de sesión principal.
El texto de la interfaz debe ser sencillo. "Usar la pantalla de bloqueo de su dispositivo" es mejor que "autenticarse con una credencial de residente FIDO2".
Errores que evitaría
No generar desafíos al cliente. El desafío se crea en el servidor y debe verificarse solo una vez.
No confíe sólo en la identificación de la credencial. Debe verificar la firma, la impugnación, el origen y Relying Party ID.
No elimine las reservas antes de tener un buen flujo de recuperación. Sin contraseña no tiene por qué convertirse en "si pierdes tu teléfono, quedarás fuera para siempre".
No trate el passkey como un botón puramente frontal. Ocultar un botón no es seguridad: la verdadera verificación es del lado del servidor.
¿Passkey-primero o passkey-amigable?
Para un producto nuevo, puede pensar en passkey-primero. Para una aplicación existente, prefiero passkey: agregue passkey como método recomendado, mida el éxito y los problemas, luego reduzca con calma el peso de la contraseña.
La migración ideal no se siente. El usuario descubre que iniciar sesión es más fácil, no que la empresa haya cambiado su protocolo de autenticación.
Conclusión
Los passkey son interesantes porque mejoran la seguridad y la UX al mismo tiempo, lo cual es poco común. No son una varita mágica: la recuperación, la compatibilidad, el soporte y la implementación aún deben diseñarse bien.
Pero el cambio básico es fuerte. Deja de pedir a los usuarios que inventen y protejan secretos. Dejas que el dispositivo firme una prueba criptográfica vinculada a tu dominio. Menos memoria humana, menos phishing, menos restablecimientos de contraseñas. Yo diría que vale la pena tomarlos en serio.