WebAuthn (Web Authentication API) es un estándar W3C que permite la autenticación segura sin contraseña mediante criptografía de clave pública. En lugar de contraseñas, los usuarios se registran y autentican usando autenticadores basados en hardware (como lectores de huellas dactilares, Face ID, YubiKeys, etc.) o autenticadores de plataforma (integrados, como Touch ID).
A continuación se explica cómo manejar el Registro y la Autenticación usando un cliente Javascript.
Cómo funciona el registro de WebAuthn
El usuario inicia el registro:
El usuario proporciona un nombre de usuario y hace clic en Registrar.
El navegador solicita opciones al servidor:
El frontend realiza una solicitud POST para obtener las opciones de registro.
El Navegador Crea Credenciales:
Usando la API navigator.credentials.create() a través de SimpleWebAuthnBrowser.startRegistration(), se crea una credencial.
El servidor verifica el registro:
El navegador envía la credencial de vuelta al servidor.
El servidor verifica el registro y almacena la clave pública para ese usuario.
El componente TsgcWSAPIServer_WebAuthn incluye un archivo HTML para probar el protocolo WebAuthn. Este archivo HTML contiene una interfaz mínima y código JavaScript para interactuar con WebAuthn.
Descripción detallada de sgcWebAuthn.html
Descripción general de la estructura:
Entrada de nombre de usuario: Captura el identificador del usuario.
Botones:
Register – inicia el registro WebAuthn.
Authenticate – inicia el inicio de sesión (gestionado de forma similar).
Debug Console: Muestra información de depuración en tiempo real (JSON de WebAuthn).
1. Interfaz HTML para entrada de datos
<input type="text" id="username" name="username" autocomplete="username webauthn" />
<button id="btnRegBegin"><strong>Register</strong></button>
2. JavaScript: Controlador de clic en botón
document.querySelector('#btnRegBegin').addEventListener('click', async () => {
const username = document.getElementById("username").value;
if (username == "") {
document.getElementById('Error').innerText = 'Please enter a username to register';
return;
}
const resp = await fetch('<#webauthn_registration_options>', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, algorithms: [] })
});
const options = await resp.json();
const attResp = await startRegistration(options); // WebAuthn API
3. Respuesta del servidor (endpoint ficticio en HTML)
fetch('/sgcWebAuthn/Registration/Options', ...)
fetch('/sgcWebAuthn/Registration/Verify', ...)
4. Finalización del registro
const verificationResp = await fetch('/webauthn/register/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(attResp)
});
const verificationJSON = await verificationResp.json();
if (verificationJSON && verificationJSON.verified) {
document.getElementById('Success').innerHTML = `Authenticator registered!`;
}
Cómo funciona la autenticación WebAuthn
El usuario inicia la autenticación:
El usuario proporciona un nombre de usuario y hace clic en Registrar.
El navegador solicita opciones al servidor:
El frontend realiza una solicitud POST para obtener las opciones de autenticación.
El Navegador Crea Credenciales:
Usando la API navigator.credentials.get() a través de SimpleWebAuthnBrowser.startAuthentication().
El servidor verifica la autenticación:
El navegador envía la credencial de vuelta al servidor.
El servidor verifica el resultado firmado.
1. Configuración de la interfaz HTML
<div class="container">
<h1>WebAuthn Authentication Sample</h1>
<section id="userdata">
<label for="username">Username:</label>
<input type="text" id="username" name="username" autocomplete="username webauthn" autofocus />
</section>
<button id="btnAuthBegin"><strong>Authenticate</strong></button>
<p id="Success" class="success"></p>
<p id="Error" class="error"></p>
<details open>
<summary>Console</summary>
<textarea id="Debug"></textarea>
</details>
</div>
2. Obtener opciones de autenticación
Antes de llamar a startAuthentication, envíe el nombre de usuario al servidor
const resp = await fetch('<#webauthn_authentication_options>', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: document.getElementById("username").value,
user_verification: 'preferred'
}),
});
El servidor responde con un objeto JSON que incluye:
{
"challenge": "base64url-encoded-random-string",
"allowCredentials": [
{
"id": "base64url-credential-id",
"type": "public-key"
}
],
"userVerification": "preferred",
"rpId": "yourdomain.com"
}
Esto se denomina PublicKeyCredentialRequestOptions.
3. Recibir la respuesta del autenticador
La respuesta asseResp tiene el siguiente aspecto (simplificada):
{
"id": "credentialId",
"rawId": "base64url-encoded-id",
"response": {
"authenticatorData": "...",
"clientDataJSON": "...",
"signature": "...",
"userHandle": "..."
},
"type": "public-key",
"clientExtensionResults": {}
}
Esta respuesta demuestra que el usuario:
4. Enviar la Respuesta de Autenticación Firmada al Servidor
Después de que el usuario interactúa con su autenticador (a través de startAuthentication()), se obtiene un objeto de respuesta en JavaScript.
const verificationResp = await fetch('/webauthn/authenticate/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(asseResp), // this is the signed response
});
5. Obtener la respuesta del servidor
El servidor responderá con un resultado como este:
{ "verified": true }
O si algo salió mal:
{ "verified": false, "error": "Invalid signature" }
Y lo gestiona en su código de frontend:
const result = await verificationResp.json();
if (result.verified) {
document.getElementById('Success').textContent = 'User authenticated!';
} else {
document.getElementById('Error').textContent = 'Authentication failed!';
}