Planteamiento y alcance
Eres propietario de un sitio web que ya cuenta con inicio de sesión mediante contraseña y deseas agregar Passkeys. En navegadores compatibles, los usuarios deben autenticarse con un desbloqueo de plataforma o una llave de seguridad, manteniendo al mismo tiempo rutas claras de respaldo y recuperación. Diseña la colaboración entre el frontend y el servidor, incluyendo navigator.credentials.create(), navigator.credentials.get(), challenges, RP IDs, comprobaciones de origen y los límites del ciclo de vida de las credenciales.
WebAuthn es una API de autenticación de clave pública entre el navegador y un autenticador. La clave privada permanece en el autenticador y el sitio web almacena la clave pública. El frontend obtiene las opciones del servidor, llama a la API del navegador, serializa el resultado y renderiza el estado; el servidor debe verificar las firmas, consumir los challenges y vincular las credenciales a las cuentas.
Qué está evaluando el entrevistador
Una respuesta sólida separa el registro y el inicio de sesión en dos flujos breves con estado: el servidor crea un challenge impredecible de un solo uso, el frontend llama al autenticador y el servidor verifica el challenge devuelto, el origen, el RP ID, la firma y el contador antes de crear una sesión. También cubre contextos seguros HTTPS, cancelación del usuario, tiempos de espera (timeouts), migración de dispositivos, múltiples credenciales y recuperación en lugar de tratar "apareció Face ID" como el éxito del protocolo.
El entrevistador notará si comprendes que el autocompletado condicional es una capacidad de UX, no un sustituto de la verificación del servidor. También podría preguntar por qué eliminar una credencial del servidor debería ir seguido de las API de señales de WebAuthn para que el autenticador pueda actualizar su estado.
Preguntas para aclarar primero
Clientes compatibles y política de cuentas
Confirma los navegadores objetivo, las plataformas móviles y de escritorio, si se permiten credenciales detectables sincronizadas y si las contraseñas o las llaves de seguridad siguen disponibles. Esas elecciones afectan a residentKey, userVerification, la selección de credenciales y el texto de ayuda.
Dominios y topología de despliegue
Confirma los subdominios de producción y de inicio de sesión, iframes, proxies inversos y dominios específicos de inquilinos (tenants). El RP ID debe ser un dominio relacionado válido para el origen actual. Un dominio de vista previa que pasa a producción no puede reutilizar la configuración por suposición de forma segura.
Recuperación y acciones de alto riesgo
Pregunta cómo demuestra el control un usuario cuando se pierden todos los autenticadores, y si el restablecimiento revoca sesiones, notifica al usuario o retrasa las acciones de alto riesgo. La recuperación es parte del ciclo de vida de la identidad; no se puede reducir a un enlace de frontend de "usar contraseña".
Estructura de respuesta en 30 segundos
"Tanto el registro como el inicio de sesión comienzan con un challenge impredecible de un solo uso generado por el servidor. El frontend pasa las opciones a WebAuthn. Durante el registro, el servidor verifica el challenge, el origen, el RP ID, la política de atestación y la clave pública antes de vincular el ID de la credencial a la cuenta. Durante el inicio de sesión, verifica la firma de aserción, el challenge, el RP ID, el origen y el contador de firmas antes de crear una sesión. El frontend distingue entre estados no compatibles, cancelados, con tiempo de espera agotado y rechazados. La mediación condicional puede proporcionar autocompletado, pero no es obligatoria para la corrección. La pérdida de un dispositivo pasa por una recuperación controlada por riesgo, revoca las sesiones antiguas y permite registrar una nueva credencial".
Solución paso a paso
Paso 1: Permitir que el servidor cree el challenge
La página de registro o de inicio de sesión solicita primero al servidor que cree un flujo. El servidor genera al menos 16 bytes de datos de challenge aleatorios y almacena su hash, cuenta, propósito, expiración y estado de consumo. El frontend no debe generarlo ni reutilizarlo: de lo contrario, una aserción antigua podría trasladarse a otro flujo. El servidor devuelve opciones de publicKey para el RP actual, y el frontend no reescribe campos sensibles a la seguridad.
Paso 2: Diseñar el registro
En un contexto seguro HTTPS, el frontend llama a navigator.credentials.create(). Las opciones incluyen el RP, ID de usuario, nombre para mostrar, algoritmos de clave pública aceptados, preferencia de verificación de usuario y política de credenciales detectables. Después de que se resuelve la Promise, el frontend serializa el ID de la credencial, los datos del cliente, la respuesta de atestación y las extensiones requeridas hacia el servidor. La clave privada nunca sale del autenticador.
Paso 3: Verificar el registro en el servidor
El servidor comprueba que el challenge coincida con el flujo, que el origen esté permitido, que el hash del RP ID sea correcto y que la firma y el algoritmo de clave pública cumplan con la política. Decide si validar la atestación en función de los objetivos de privacidad y compatibilidad. En caso de éxito, almacena únicamente la clave pública, el ID de la credencial, la cuenta, el contador de firmas y la etiqueta de dispositivo necesaria. No debe retener el objeto sin procesar completo ni datos identificables del dispositivo de forma permanente.
Paso 4: Diseñar el inicio de sesión y el autocompletado condicional
Para el inicio de sesión, el servidor crea un nuevo challenge y el frontend llama a navigator.credentials.get() con él, enviando luego la aserción. El servidor verifica el challenge, el origen, el RP ID, la firma y el contador antes de resolver la cuenta a partir del ID de la credencial. Con la mediación condicional, la página puede solicitar credenciales detectables después de que el usuario interactúe con el campo de nombre de usuario, lo que permite al navegador mostrar Passkeys en el autocompletado. Esto cambia la UX del descubrimiento, no el contrato de verificación.
Paso 5: Gestionar el estado del frontend y los fallos
La cancelación, el tiempo de espera agotado, los navegadores no compatibles, la política de permisos bloqueada y el rechazo del servidor deben convertirse en estados distintos y comprensibles para el usuario. AbortController puede cancelar una llamada pendiente cuando el usuario abandona la página o hace clic nuevamente, evitando que una Promise antigua sobrescriba el estado actual. El frontend no debe mostrar firmas, IDs de credenciales ni errores internos del servidor, y nunca debe marcar un registro incompleto como exitoso.
Paso 6: Recuperación, revocación y gestión de credenciales
La configuración de la cuenta debe listar las etiquetas de las credenciales, la hora de creación y la hora de último uso, y permitir eliminar una credencial. Después de que el servidor la elimina, un ID de credencial desconocido posterior puede activar PublicKeyCredential.signalUnknownCredential(); un inicio de sesión exitoso puede usar signalAllAcceptedCredentials() para sincronizar los IDs que el servidor aún acepta. Perder todos los autenticadores utiliza una recuperación controlada por riesgo, como una contraseña más comprobaciones de riesgo, un correo electrónico verificado o una revisión de soporte. Revoca las sesiones antiguas y luego exige el registro de una nueva Passkey.
Paso 7: Verificar y evolucionar
Prueba diferentes navegadores, autenticadores de plataforma, credenciales sincronizadas, llaves de seguridad, cancelaciones, tiempos de espera, orígenes incorrectos, challenges expirados y repetidos, anomalías en el contador y credenciales antiguas posteriores a la recuperación. Detecta capacidades y recibe políticas del servidor en lugar de adivinar a partir de cadenas de user-agent. Las nuevas extensiones deben ser opcionales con una UI de respaldo, y una extensión desconocida no debe alterar la verificación central del servidor.
Respuesta de muestra de alta calidad
Trataría a Passkey como un flujo de autenticación de clave pública guiado por el servidor. Cuando un usuario inicia el registro, el frontend solicita un challenge de un solo uso y opciones de publicKey al servidor, luego llama a WebAuthn a través de HTTPS. El servidor por sí solo verifica el challenge, el origen, el RP ID, la firma, el algoritmo y cualquier atestación requerida, y luego almacena el ID de la credencial, la clave pública, el contador y la vinculación de la cuenta. La clave privada permanece en el autenticador.
El inicio de sesión comienza con otro challenge del servidor. El frontend llama a navigator.credentials.get(), envía la aserción y el servidor verifica su firma, challenge, origen, RP ID y contador antes de crear una sesión. Si se admite la mediación condicional, ofrezco el autocompletado de Passkey después de la interacción con el campo de nombre de usuario, pero este sigue la misma verificación del servidor.
El frontend distingue los estados no compatibles, cancelados, con tiempo de espera agotado y rechazados, y cancela las llamadas pendientes cuando se abandona la página. La configuración de la cuenta lista y elimina credenciales; después de la eliminación, las señales de WebAuthn evitan que el navegador sugiera una credencial desconocida. Perder un dispositivo requiere una recuperación controlada por riesgo, revoca sesiones antiguas, notifica al usuario y registra una credencial de reemplazo. Las pruebas cubren autenticadores multiplataforma, orígenes incorrectos, challenges expirados o repetidos, anomalías en el contador, respaldo del navegador y credenciales antiguas tras la recuperación para que los cambios de UX nunca debiliten la verificación del protocolo.
Errores comunes
- Error: Generar el challenge en el frontend o colocarlo en una constante de la página. → Por qué falla: Un atacante puede reproducir una aserción antigua y el servidor no puede establecer la frescura del flujo. → Solución: Generarlo en el servidor, almacenarlo brevemente, consumirlo una sola vez y vincularlo a la cuenta y al propósito.
- Error: Iniciar la sesión del usuario cuando la Promise se resuelve. → Por qué falla: Un objeto del navegador no es prueba de que el servidor haya verificado el origen, el RP ID y la firma. → Solución: Completar la validación de la aserción en el servidor y permitir que el frontend renderice resultados estructurados.
- Error: Detectar compatibilidad con cadenas de user-agent. → Por qué falla: Las versiones de los navegadores, los autenticadores de plataforma y las políticas de permisos varían. → Solución: Usar APIs de capacidades y políticas del servidor, y luego proporcionar una ruta explícita mediante contraseña o llave de seguridad.
- Error: Eliminar la credencial de la base de datos sin sincronizar el estado del dispositivo. → Por qué falla: El autenticador sigue creyendo que la credencial existe y continúa ofreciendo una opción inutilizable. → Solución: Llamar a las API de señales de WebAuthn en un estado autenticado adecuado y proporcionar una ruta de re-registro.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Por qué el RP ID no puede ser cualquier dominio de API?
El RP ID debe ser un dominio relacionado para el origen actual y es verificado tanto por el navegador como por el servidor. Un valor no relacionado hace que la creación falle o expande el alcance de confianza. Para dominios multi-tenant, configura explícitamente cada dominio de confianza; nunca aceptes una cadena arbitraria proporcionada por el cliente.
Pregunta de seguimiento 2: El usuario se registró en un teléfono, pero el escritorio no tiene llave. ¿Qué ocurre ahora?
Si se permiten credenciales detectables sincronizadas, el navegador y el administrador de credenciales pueden proporcionar la Passkey de la misma cuenta en todos los dispositivos. De lo contrario, ofrece un flujo de código QR entre dispositivos o una llave de seguridad. El frontend debe indicar que el dispositivo no puede usar la credencial y ofrecer una acción; el servidor continúa verificando el mismo challenge y las reglas de aserción, incluido el origen.
Pregunta de seguimiento 3: ¿Debería una reducción del contador (rollback) bloquear la cuenta de inmediato?
Primero distingue entre credenciales de plataforma sincronizadas, restauración de copias de seguridad y un riesgo real de clonación. Trata una anomalía como una señal de riesgo que puede requerir verificación adicional y notificar al usuario, en lugar de bloquearlo permanentemente sin contexto. Las acciones de alto valor pueden requerir otra credencial registrada o revisión por parte de soporte, registrando la decisión para ajustar las políticas.
Pregunta de seguimiento 4: ¿La recuperación debilita la seguridad de Passkey?
Lo hace si la recuperación se basa únicamente en un enlace de correo electrónico potencialmente comprometido. Utiliza comprobaciones de riesgo por niveles, revocación de sesiones, un período de enfriamiento, notificaciones y retraso de acciones de alto riesgo. Después de la recuperación, registra una nueva credencial y elimina las antiguas. Explica el balance entre disponibilidad y usurpación de cuenta (account takeover) en lugar de prometer que los usuarios nunca perderán el acceso.