Webauthn | Klient JavaScript

WebAuthn (Web Authentication API) to standard W3C umożliwiający bezpieczne uwierzytelnianie bez hasła przy użyciu kryptografii klucza publicznego. Zamiast haseł użytkownicy rejestrują się i uwierzytelniają przy użyciu sprzętowych urządzeń uwierzytelniających (takich jak czytniki linii papilarnych, Face ID, YubiKeys itp.) lub uwierzytelniatorów platformowych (wbudowanych, jak Touch ID).

 

Poniżej przedstawiono, jak obsługiwać rejestrację i uwierzytelnianie przy użyciu klienta JavaScript.

Rejestracja WebAuthn

Jak działa rejestracja WebAuthn

 

 

 

Komponent TsgcWSAPIServer_WebAuthn zawiera plik HTML służący do testowania protokołu WebAuthn. Plik HTML zawiera minimalny interfejs użytkownika oraz kod JavaScript umożliwiający interakcję z WebAuthn.

 

Omówienie pliku sgcWebAuthn.html

 

Przegląd struktury:

 

 

 

1. Interfejs HTML do wprowadzania danych

 

<input type="text" id="username" name="username" autocomplete="username webauthn" />
<button id="btnRegBegin"><strong>Register</strong></button>

 

2. JavaScript: procedura obsługi kliknięcia przycisku

 

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. Odpowiedź serwera (fałszywy punkt końcowy w HTML)

 

fetch('/sgcWebAuthn/Registration/Options', ...)

fetch('/sgcWebAuthn/Registration/Verify', ...)

 

4. Finalizowanie rejestracji

 

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!`;
}

 

 

 

Uwierzytelnianie WebAuthn

Jak działa uwierzytelnianie WebAuthn

 

 

1. Konfiguracja interfejsu 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. Pobierz opcje uwierzytelniania

 

Przed wywołaniem startAuthentication należy wysłać nazwę użytkownika do serwera

 

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'
  }),
});

 

Serwer odpowiada obiektem JSON zawierającym:

 

{
  "challenge": "base64url-encoded-random-string",
  "allowCredentials": [
    {
      "id": "base64url-credential-id",
      "type": "public-key"
    }
  ],
  "userVerification": "preferred",
  "rpId": "yourdomain.com"
}

 

Jest to PublicKeyCredentialRequestOptions.

 

 

3. Odbierz odpowiedź uwierzytelniającą

 

Odpowiedź asseResp wygląda następująco (uproszczona):

 

{
  "id": "credentialId",
  "rawId": "base64url-encoded-id",
  "response": {
    "authenticatorData": "...",
    "clientDataJSON": "...",
    "signature": "...",
    "userHandle": "..."
  },
  "type": "public-key",
  "clientExtensionResults": {}
}

 

Odpowiedź ta dowodzi, że użytkownik:

 

 

 

4. Wyślij podpisaną odpowiedź uwierzytelniającą do serwera

 

Po interakcji użytkownika z jego uwierzytelniaczem (za pomocą startAuthentication()), w języku JavaScript otrzymywany jest obiekt odpowiedzi.

 

const verificationResp = await fetch('/webauthn/authenticate/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(asseResp), // this is the signed response
});

 

 

5. Pobierz odpowiedź serwera

 

Serwer odpowie wynikiem w następującej postaci:

 

{ "verified": true }

 

Lub jeśli coś poszło nie tak:

 

{ "verified": false, "error": "Invalid signature" }

 

Obsługuje się to w kodzie frontendowym w następujący sposób:

 

const result = await verificationResp.json();

if (result.verified) {
  document.getElementById('Success').textContent = 'User authenticated!';
} else {
  document.getElementById('Error').textContent = 'Authentication failed!';
}