WebAuthn (Web Authentication API) è uno standard W3C che consente un'autenticazione sicura senza password utilizzando la crittografia a chiave pubblica. Invece delle password, gli utenti si registrano e si autenticano utilizzando authenticator basati su hardware (come lettori di impronte digitali, Face ID, YubiKey, ecc.) o authenticator di piattaforma (integrati, come Touch ID).
Di seguito è illustrato come gestire la Registrazione e l'Autenticazione utilizzando un client Javascript.
Come funziona la registrazione WebAuthn
L'utente avvia la registrazione:
L'utente fornisce un nome utente e clicca su Registra.
Il browser richiede le opzioni al server:
Il frontend invia una richiesta POST per ottenere le opzioni di registrazione.
Il browser crea le credenziali:
Utilizzando l'API navigator.credentials.create() tramite SimpleWebAuthnBrowser.startRegistration(), viene creata una credenziale.
Il server verifica la registrazione:
Il browser invia le credenziali al server.
Il server verifica la registrazione e memorizza la chiave pubblica per quell'utente.
Il componente TsgcWSAPIServer_WebAuthn include un file HTML per testare il protocollo WebAuthn. Questo file HTML contiene un'interfaccia utente minimale e JavaScript per interagire con WebAuthn.
Analisi dettagliata di sgcWebAuthn.html
Panoramica della struttura:
Input Nome Utente: Acquisisce l'identificatore dell'utente.
Pulsanti:
Register – avvia la registrazione WebAuthn.
Authenticate – avvia il login (gestito in modo simile).
Console di debug: mostra informazioni di debug in tempo reale (JSON da WebAuthn).
1. Interfaccia HTML per l'Input
<input type="text" id="username" name="username" autocomplete="username webauthn" />
<button id="btnRegBegin"><strong>Register</strong></button>
2. JavaScript: Gestore clic sul pulsante
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. Risposta del server (endpoint fittizio in HTML)
fetch('/sgcWebAuthn/Registration/Options', ...)
fetch('/sgcWebAuthn/Registration/Verify', ...)
4. Finalizzazione della registrazione
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!`;
}
Come funziona l'autenticazione WebAuthn
L'utente avvia l'autenticazione:
L'utente fornisce un nome utente e clicca su Registra.
Il browser richiede le opzioni al server:
Il frontend effettua una richiesta POST per ottenere le opzioni di autenticazione.
Il browser crea le credenziali:
Utilizzando l'API navigator.credentials.get() tramite SimpleWebAuthnBrowser.startAuthentication().
Il server verifica l'autenticazione:
Il browser rinvia la credenziale al server.
Il server verifica il risultato firmato.
1. Configurazione UI 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. Ottieni le opzioni di autenticazione
Prima di chiamare startAuthentication, si invia lo username al server
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'
}),
});
Il server risponde con un oggetto JSON che include:
{
"challenge": "base64url-encoded-random-string",
"allowCredentials": [
{
"id": "base64url-credential-id",
"type": "public-key"
}
],
"userVerification": "preferred",
"rpId": "yourdomain.com"
}
Questo viene chiamato PublicKeyCredentialRequestOptions.
3. Ricezione della risposta dell'autenticatore
Il asseResp si presenta così (semplificato):
{
"id": "credentialId",
"rawId": "base64url-encoded-id",
"response": {
"authenticatorData": "...",
"clientDataJSON": "...",
"signature": "...",
"userHandle": "..."
},
"type": "public-key",
"clientExtensionResults": {}
}
Questa risposta dimostra che l'utente:
4. Inviare la risposta di autenticazione firmata al server
Dopo che l'utente interagisce con il proprio authenticator (tramite startAuthentication()), ottiene un oggetto response in 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. Ottenere la risposta del server
Il server risponderà con un risultato simile a questo:
{ "verified": true }
Oppure se qualcosa è andato storto:
{ "verified": false, "error": "Invalid signature" }
E lo si gestisce nel codice frontend:
const result = await verificationResp.json();
if (result.verified) {
document.getElementById('Success').textContent = 'User authenticated!';
} else {
document.getElementById('Error').textContent = 'Authentication failed!';
}