Crea la tua prima app WebAuthn

1. Prima di iniziare

L'API Web Authentication, nota anche come WebAuthn, ti consente di creare e utilizzare le credenziali della chiave pubblica con ambito originale per autenticare gli utenti.

L'API supporta l'utilizzo di identificatori BLE, NFC e USB-roaming U2F o FIDO2, chiamati anche token di sicurezza, e un autenticatore di piattaforma che consente agli utenti di autenticarsi con le loro impronte digitali o i blocchi schermo.

In questo codelab, creerai un sito web con una semplice funzionalità di riautenticazione che utilizza un sensore di impronte digitali. La riautenticazione protegge i dati dell'account perché richiede agli utenti che hanno già eseguito l'accesso a un sito web di autenticarsi di nuovo quando tentano di inserire sezioni importanti del sito web o di visitarlo di nuovo dopo un determinato periodo di tempo.

Prerequisiti

  • Nozioni di base sul funzionamento di WebAuthn
  • Competenze di programmazione di base con JavaScript

In questo lab proverai a:

  • Crea un sito web con una semplice funzionalità di riautenticazione che utilizza un sensore di impronte digitali

Che cosa ti serve

  • Uno dei seguenti dispositivi:
    • Un dispositivo Android, preferibilmente con un sensore biometrico
    • Un iPhone o un iPad con Touch ID o Face ID su iOS 14 o versioni successive
    • Un MacBook Pro o Air con Touch ID su macOS Big Sur o superiore
    • Windows 10 19H1 o versioni successive con Windows Hello configurato
  • Uno dei seguenti browser:
    • Google Chrome 67 o versioni successive
    • Microsoft Edge 85 o versioni successive
    • Safari 14 o versioni successive

2. Configura

In questo codelab, utilizzi un servizio chiamato glitch. Qui puoi modificare il codice lato client e lato server con JavaScript ed eseguirne il deployment istantaneo.

Vai a https://glitch.com/edit/#!/webauthn-codelab-start.

Guarda come funziona

Per visualizzare lo stato iniziale del sito web:

  1. Fai clic su 62bb7a6aac381af8.png Mostra > 3343769d04c09851.png In una nuova finestra per visualizzare il sito web attivo.
  2. Inserisci il nome utente che preferisci e fai clic su Avanti.
  3. Inserisci una password e fai clic su Accedi.

La password viene ignorata, ma sei ancora autenticato. Arrivi nella home page.

  1. Fai clic su Prova a ripetere l'autenticazione e ripeti il secondo, il terzo e il quarto passaggio.
  2. Fai clic su Esci.

Tieni presente che devi inserire la password ogni volta che provi ad accedere. Questo emula un utente che deve ripetere l'autenticazione prima di poter accedere a una sezione importante di un sito web.

Esegui il remix del codice

  1. Vai al codelab dell'API WebAuthn/FIDO2.
  2. Fai clic sul nome del progetto > Remix Project 306122647ce93305.png per creare un fork del progetto e continuare con la tua versione in corrispondenza di un nuovo URL.

8d42bd24f0fd185c.png

3. Registrare una credenziale con un'impronta

Devi registrare una credenziale generata da un UVPA, un autenticatore integrato nel dispositivo e che verifichi l'identità dell'utente. In genere questo sensore viene visto come un sensore di impronte digitali a seconda del dispositivo dell'utente.

Aggiungi questa funzionalità alla pagina /home:

260aab9f1a2587a7.png

Crea funzione registerCredential()

Creare una funzione registerCredential(), che registra una nuova credenziale.

public/client.js

export const registerCredential = async () => {

};

Ottieni la sfida e altre opzioni dall'endpoint del server

Prima di chiedere all'utente di registrare una nuova credenziale, richiedi al server di restituire i parametri per passare a WebAuthn, inclusa una verifica. Fortunatamente, disponi già di un endpoint del server che risponde con questi parametri.

Aggiungi il seguente codice a registerCredential().

public/client.js

const opts = {
  attestation: 'none',
  authenticatorSelection: {
    authenticatorAttachment: 'platform',
    userVerification: 'required',
    requireResidentKey: false
  }
};

const options = await _fetch('/auth/registerRequest', opts);

Il protocollo tra un server e un client non fa parte della specifica di WebAuthn. Tuttavia, questo codelab è progettato per allinearsi alla specifica WebAuthn e l'oggetto JSON che trasmetti al server è molto simile a PublicKeyCredentialCreationOptions, quindi è intuitivo per te. La seguente tabella contiene i parametri importanti che puoi trasferire al server e spiega cosa fanno:

Parametri

Descrizioni

attestation

Preferenza per l'emissione dell'attestazione: none, indirect o direct. Scegli none, a meno che non ti serva.

excludeCredentials

Array di PublicKeyCredentialDescriptor in modo che l'autenticatore possa evitare di creare duplicati.

authenticatorSelection

authenticatorAttachment

Filtra autenticatori disponibili. Se vuoi collegare un autenticatore al dispositivo, usa"platform". Per gli autenticatori in roaming, usa"cross-platform".

userVerification

Determina se la verifica degli utenti locali dell'autenticatore è "required", "preferred" o "discouraged". Se vuoi l'autenticazione tramite impronta digitale o blocco schermo, utilizza "required".

requireResidentKey

Usa true se le credenziali create devono essere disponibili per l'esperienza utente del selettore account futuro.

Per ulteriori informazioni su queste opzioni, vedi 5.4. Opzioni per la creazione di credenziali (dizionario PublicKeyCredentialCreationOptions).

Di seguito sono riportate alcune opzioni di esempio che riceverai dal server.

{
  "rp": {
    "name": "WebAuthn Codelab",
    "id": "webauthn-codelab.glitch.me"
  },
  "user": {
    "displayName": "User Name",
    "id": "...",
    "name": "test"
  },
  "challenge": "...",
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    }, {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [
    {
      "id": "...",
      "type": "public-key",
      "transports": [
        "internal"
      ]
    }
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "userVerification": "required"
  }
}

Crea una credenziale

  1. Poiché queste opzioni vengono pubblicate codificate per la transizione tramite protocollo HTTP, converti alcuni parametri in formato binario, nello specifico, user.id, challenge e istanze di id incluse nell'array di excludeCredentials:

public/client.js

options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);

if (options.excludeCredentials) {
  for (let cred of options.excludeCredentials) {
    cred.id = base64url.decode(cred.id);
  }
}
  1. Richiama il metodo navigator.credentials.create() per creare una nuova credenziale.

Con questa chiamata, il browser interagisce con l'autenticatore e tenta di verificare l'identità dell'utente con l'UVPA.

public/client.js

const cred = await navigator.credentials.create({
  publicKey: options,
});

Una volta che l'utente ha verificato la propria identità, dovresti ricevere un oggetto di credenziali che puoi inviare al server e registrare l'autenticatore.

Registra le credenziali nell'endpoint del server

Di seguito è riportato un esempio di oggetto di credenziali che dovresti aver ricevuto.

{
  "id": "...",
  "rawId": "...",
  "type": "public-key",
  "response": {
    "clientDataJSON": "...",
    "attestationObject": "..."
  }
}
  1. Come quando hai ricevuto un oggetto opzione per la registrazione di una credenziale, codifica i parametri binari della credenziale in modo che possano essere inviati al server come stringa:

public/client.js

const credential = {};
credential.id = cred.id;
credential.rawId = base64url.encode(cred.rawId);
credential.type = cred.type;

if (cred.response) {
  const clientDataJSON =
    base64url.encode(cred.response.clientDataJSON);
  const attestationObject =
    base64url.encode(cred.response.attestationObject);
  credential.response = {
    clientDataJSON,
    attestationObject,
  };
}
  1. Archivia localmente l'ID credenziali in modo da poterlo utilizzare per l'autenticazione quando l'utente torna:

public/client.js

localStorage.setItem(`credId`, credential.id);
  1. Invia l'oggetto al server e, se restituisce HTTP code 200, considera la nuova credenziale come registrata correttamente.

public/client.js

return await _fetch('/auth/registerResponse' , credential);

Ora hai la funzione registerCredential() completa.

Codice finale per questa sezione

public/client.js

...
export const registerCredential = async () => {
  const opts = {
    attestation: 'none',
    authenticatorSelection: {
      authenticatorAttachment: 'platform',
      userVerification: 'required',
      requireResidentKey: false
    }
  };

  const options = await _fetch('/auth/registerRequest', opts);

  options.user.id = base64url.decode(options.user.id);
  options.challenge = base64url.decode(options.challenge);

  if (options.excludeCredentials) {
    for (let cred of options.excludeCredentials) {
      cred.id = base64url.decode(cred.id);
    }
  }
  
  const cred = await navigator.credentials.create({
    publicKey: options
  });

  const credential = {};
  credential.id =     cred.id;
  credential.rawId =  base64url.encode(cred.rawId);
  credential.type =   cred.type;

  if (cred.response) {
    const clientDataJSON =
      base64url.encode(cred.response.clientDataJSON);
    const attestationObject =
      base64url.encode(cred.response.attestationObject);
    credential.response = {
      clientDataJSON,
      attestationObject
    };
  }

  localStorage.setItem(`credId`, credential.id);
  
  return await _fetch('/auth/registerResponse' , credential);
};
...

4. Crea l'UI per registrare, ricevere e rimuovere le credenziali

È utile avere un elenco di credenziali e pulsanti registrati per rimuoverli.

9b5b5ae4a7b316bd.png

Segnaposto per UI build

Aggiungi l'interfaccia utente per elencare le credenziali e un pulsante per registrare una nuova credenziale. A seconda della disponibilità o meno della funzionalità, rimuovi la classe hidden dal messaggio di avviso o dal pulsante per registrare una nuova credenziale. ul#list è il segnaposto per l'aggiunta di un elenco di credenziali registrate.

visualizzazioni/home.html

<p id="uvpa_unavailable" class="hidden">
  This device does not support User Verifying Platform Authenticator. You can't register a credential.
</p>
<h3 class="mdc-typography mdc-typography--headline6">
  Your registered credentials:
</h3>
<section>
  <div id="list"></div>
</section>
<mwc-button id="register" class="hidden" icon="fingerprint" raised>Add a credential</mwc-button>

Rilevamento funzionalità e disponibilità UVPA

Segui questi passaggi per verificare la disponibilità dell'UVPA:

  1. Controlla window.PublicKeyCredential per verificare se WebAuthn è disponibile.
  2. Chiama il numero PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() per verificare se è disponibile un filtro UVPA . Se sono disponibili, devi mostrare il pulsante per registrare una nuova credenziale. Se una di queste opzioni non è disponibile, verrà visualizzato il messaggio di avviso.

visualizzazioni/home.html

const register = document.querySelector('#register');

if (window.PublicKeyCredential) {
  PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
  .then(uvpaa => {
    if (uvpaa) {
      register.classList.remove('hidden');
    } else {
      document
        .querySelector('#uvpa_unavailable')
        .classList.remove('hidden');
    }
  });        
} else {
  document
    .querySelector('#uvpa_unavailable')
    .classList.remove('hidden');
}

Recupera e visualizza un elenco di credenziali

  1. Crea una funzione getCredentials() in modo da poter recuperare le credenziali registrate e visualizzarle in un elenco. Fortunatamente, hai già un pratico endpoint sul server /auth/getKeys da cui puoi recuperare le credenziali registrate per l'utente che ha eseguito l'accesso.

Il file JSON restituito include informazioni sulle credenziali, come id e publicKey. Puoi creare codice HTML per mostrarlo all'utente.

visualizzazioni/home.html

const getCredentials = async () => {
  const res = await _fetch('/auth/getKeys');
  const list = document.querySelector('#list');
  const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
    <div class="mdc-card credential">
      <span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
      <pre class="public-key">${cred.publicKey}</pre>
      <div class="mdc-card__actions">
        <mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
      </div>
    </div>`) : html`
    <p>No credentials found.</p>
    `}`;
  render(creds, list);
};
  1. Richiama getCredentials() per visualizzare le credenziali disponibili non appena l'utente arriva alla pagina /home.

visualizzazioni/home.html

getCredentials();

Rimuovi la credenziale

Nell'elenco delle credenziali, hai aggiunto un pulsante per rimuovere ogni credenziale. Puoi inviare una richiesta a /auth/removeKey insieme al parametro di ricerca credId per rimuoverla.

public/client.js

export const unregisterCredential = async (credId) => {
  localStorage.removeItem('credId');
  return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
  1. Aggiungi unregisterCredential all'istruzione import esistente.

visualizzazioni/home.html

import { _fetch, unregisterCredential } from '/client.js';
  1. Aggiungi una funzione da chiamare quando l'utente fa clic su Rimuovi.

visualizzazioni/home.html

const removeCredential = async e => {
  try {
    await unregisterCredential(e.target.id);
    getCredentials();
  } catch (e) {
    alert(e);
  }
};

Registra una credenziale

Puoi chiamare registerCredential() per registrare una nuova credenziale quando l'utente fa clic su Add credential (Aggiungi una credenziale).

  1. Aggiungi registerCredential all'istruzione import esistente.

visualizzazioni/home.html

import { _fetch, registerCredential, unregisterCredential } from '/client.js';
  1. Richiama registerCredential() con le opzioni per navigator.credentials.create().

Non dimenticarti di rinnovare l'elenco delle credenziali chiamando getCredentials() dopo la registrazione.

visualizzazioni/home.html

register.addEventListener('click', e => {
  registerCredential().then(user => {
    getCredentials();
  }).catch(e => alert(e));
});

Ora dovresti essere in grado di registrare una nuova credenziale e visualizzare le relative informazioni. Puoi provarlo sul tuo sito web attivo.

Codice finale per questa sezione

visualizzazioni/home.html

...
      <p id="uvpa_unavailable" class="hidden">
        This device does not support User Verifying Platform Authenticator. You can't register a credential.
      </p>
      <h3 class="mdc-typography mdc-typography--headline6">
        Your registered credentials:
      </h3>
      <section>
        <div id="list"></div>
        <mwc-fab id="register" class="hidden" icon="add"></mwc-fab>
      </section>
      <mwc-button raised><a href="/reauth">Try reauth</a></mwc-button>
      <mwc-button><a href="/auth/signout">Sign out</a></mwc-button>
    </main>
    <script type="module">
      import { _fetch, registerCredential, unregisterCredential } from '/client.js';
      import { html, render } from 'https://unpkg.com/lit-html@1.0.0/lit-html.js?module';

      const register = document.querySelector('#register');

      if (window.PublicKeyCredential) {
        PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
        .then(uvpaa => {
          if (uvpaa) {
            register.classList.remove('hidden');
          } else {
            document
              .querySelector('#uvpa_unavailable')
              .classList.remove('hidden');
          }
        });        
      } else {
        document
          .querySelector('#uvpa_unavailable')
          .classList.remove('hidden');
      }

      const getCredentials = async () => {
        const res = await _fetch('/auth/getKeys');
        const list = document.querySelector('#list');
        const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
          <div class="mdc-card credential">
            <span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
            <pre class="public-key">${cred.publicKey}</pre>
            <div class="mdc-card__actions">
              <mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
            </div>
          </div>`) : html`
          <p>No credentials found.</p>
          `}`;
        render(creds, list);
      };

      getCredentials();

      const removeCredential = async e => {
        try {
          await unregisterCredential(e.target.id);
          getCredentials();
        } catch (e) {
          alert(e);
        }
      };

      register.addEventListener('click', e => {
        registerCredential({
          attestation: 'none',
          authenticatorSelection: {
            authenticatorAttachment: 'platform',
            userVerification: 'required',
            requireResidentKey: false
          }
        })
        .then(user => {
          getCredentials();
        })
        .catch(e => alert(e));
      });
    </script>
...

public/client.js

...
export const unregisterCredential = async (credId) => {
  localStorage.removeItem('credId');
  return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
...

5. Autenticare l'utente con un'impronta

Ora hai delle credenziali registrate e pronte per essere utilizzate per autenticare l'utente. Ora aggiungi la funzionalità di riautenticazione al sito web. Ecco l'esperienza utente:

Quando un utente arriva alla pagina /reauth, visualizza il pulsante Autentica se è possibile eseguire l'autenticazione biometrica. L'autenticazione con impronta (UVPA) inizia quando tocca Autentica, esegue l'autenticazione e arriva alla pagina /home. Se l'autenticazione biometrica non è disponibile o un'autenticazione con biometria non riesce, l'interfaccia utente utilizza il modulo password esistente.

b8770c4e7475b075.png

Crea funzione authenticate()

Crea una funzione denominata authenticate(), che verifichi l'identità dell'utente con un'impronta. Aggiungi il codice JavaScript qui:

public/client.js

export const authenticate = async () => {

};

Ottieni la sfida e altre opzioni dall'endpoint del server

  1. Prima di eseguire l'autenticazione, verifica se l'utente dispone di un ID credenziale memorizzato e, se lo fa, impostalo come parametro di ricerca.

Quando fornisci un ID credenziale insieme ad altre opzioni, il server può fornire allowCredentials pertinenti e la verifica dell'utente è affidabile.

public/client.js

const opts = {};

let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
  url += `?credId=${encodeURIComponent(credId)}`;
}
  1. Prima di chiedere all'utente di autenticarsi, chiedi al server di inviare una verifica e altri parametri. Richiama _fetch() con opts come argomento per inviare una richiesta POST al server.

public/client.js

const options = await _fetch(url, opts);

Ecco alcune opzioni che dovresti ricevere (in linea con PublicKeyCredentialRequestOptions).

{
  "challenge": "...",
  "timeout": 1800000,
  "rpId": "webauthn-codelab.glitch.me",
  "userVerification": "required",
  "allowCredentials": [
    {
      "id": "...",
      "type": "public-key",
      "transports": [
        "internal"
      ]
    }
  ]
}

L'opzione più importante è allowCredentials. Quando ricevi le opzioni dal server, allowCredentials deve essere un singolo oggetto in un array o un array vuoto a seconda che le credenziali con ID nel parametro di ricerca si trovino sul lato server.

  1. Risolvi la promessa con null quando allowCredentials è un array vuoto in modo che l'interfaccia utente si ripeta per richiedere una password.
if (options.allowCredentials.length === 0) {
  console.info('No registered credentials found.');
  return Promise.resolve(null);
}

Verifica a livello locale l'utente e ricevi una credenziale

  1. Poiché queste opzioni vengono pubblicate codificate per poter utilizzare il protocollo HTTP, converti alcuni parametri in formato binario, nello specifico challenge e le istanze di id incluse nell'array allowCredentials:

public/client.js

options.challenge = base64url.decode(options.challenge);

for (let cred of options.allowCredentials) {
  cred.id = base64url.decode(cred.id);
}
  1. Chiama il metodo navigator.credentials.get() per verificare l'identità dell'utente con un filtro UVPA.

public/client.js

const cred = await navigator.credentials.get({
  publicKey: options
});

Una volta che l'utente ha verificato la propria identità, dovresti ricevere un oggetto di credenziali che puoi inviare al server e autenticare l'utente.

Verifica la credenziale

Ecco un esempio dell'oggetto PublicKeyCredential (response è AuthenticatorAssertionResponse) che dovresti aver ricevuto:

{
  "id": "...",
  "type": "public-key",
  "rawId": "...",
  "response": {
    "clientDataJSON": "...",
    "authenticatorData": "...",
    "signature": "...",
    "userHandle": ""
  }
}
  1. Codifica i parametri binari delle credenziali in modo che possano essere inviati al server come stringa:

public/client.js

const credential = {};
credential.id = cred.id;
credential.type = cred.type;
credential.rawId = base64url.encode(cred.rawId);

if (cred.response) {
  const clientDataJSON =
    base64url.encode(cred.response.clientDataJSON);
  const authenticatorData =
    base64url.encode(cred.response.authenticatorData);
  const signature =
    base64url.encode(cred.response.signature);
  const userHandle =
    base64url.encode(cred.response.userHandle);
  credential.response = {
    clientDataJSON,
    authenticatorData,
    signature,
    userHandle,
  };
}
  1. Invia l'oggetto al server e, se restituisce HTTP code 200, considera che l'utente abbia eseguito l'accesso:

public/client.js

return await _fetch(`/auth/signinResponse`, credential);

Ora hai la funzione authentication() completa.

Codice finale per questa sezione

public/client.js

...
export const authenticate = async () => {
  const opts = {};

  let url = '/auth/signinRequest';
  const credId = localStorage.getItem(`credId`);
  if (credId) {
    url += `?credId=${encodeURIComponent(credId)}`;
  }
  
  const options = await _fetch(url, opts);
  
  if (options.allowCredentials.length === 0) {
    console.info('No registered credentials found.');
    return Promise.resolve(null);
  }

  options.challenge = base64url.decode(options.challenge);

  for (let cred of options.allowCredentials) {
    cred.id = base64url.decode(cred.id);
  }

  const cred = await navigator.credentials.get({
    publicKey: options
  });

  const credential = {};
  credential.id = cred.id;
  credential.type = cred.type;
  credential.rawId = base64url.encode(cred.rawId);

  if (cred.response) {
    const clientDataJSON =
      base64url.encode(cred.response.clientDataJSON);
    const authenticatorData =
      base64url.encode(cred.response.authenticatorData);
    const signature =
      base64url.encode(cred.response.signature);
    const userHandle =
      base64url.encode(cred.response.userHandle);
    credential.response = {
      clientDataJSON,
      authenticatorData,
      signature,
      userHandle,
    };
  }

  return await _fetch(`/auth/signinResponse`, credential);
};
...

6. Abilita l'esperienza di riautenticazione

UI della build

Quando l'utente torna, vuoi che venga autenticata nel modo più semplice e sicuro possibile. È qui che arriva l'autenticazione biometrica. Tuttavia, esistono casi in cui l'autenticazione biometrica potrebbe non funzionare:

  • L'UVPA non è disponibile.
  • L'utente non ha ancora registrato alcuna credenziale sul dispositivo.
  • Lo spazio di archiviazione è stato cancellato e il dispositivo non ricorda più l'ID credenziale.
  • L'utente non è in grado di verificare la propria identità per qualche motivo, ad esempio quando il dito è bagnato o indossa una mascherina.

Ecco perché è sempre importante fornire altre opzioni di accesso di riserva. In questo codelab, utilizzerai la soluzione password basata su modulo.

19da999b0145054.png

  1. Aggiungi l'interfaccia utente per mostrare un pulsante di autenticazione che richiama l'autenticazione biometrica in aggiunta al modulo password.

Utilizza la classe hidden per mostrarne e nasconderne una in modo selettivo, a seconda dello stato dell'utente.

views/reauth.html

<div id="uvpa_available" class="hidden">
  <h2>
    Verify your identity
  </h2>
  <div>
    <mwc-button id="reauth" raised>Authenticate</mwc-button>
  </div>
  <div>
    <mwc-button id="cancel">Sign-in with password</mwc-button>
  </div>
</div>
  1. Aggiungi class="hidden" al modulo:

views/reauth.html

<form id="form" method="POST" action="/auth/password" class="hidden">

Rilevamento funzionalità e disponibilità UVPA

Gli utenti devono accedere con una password se si verifica una delle seguenti condizioni:

  • WebAuthn non è disponibile.
  • L'UVPA non è disponibile.
  • Impossibile trovare un ID credenziale per questo UVPA.

Mostra il pulsante di autenticazione in modo selettivo o nascondilo:

views/reauth.html

if (window.PublicKeyCredential) {
  PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
  .then(uvpaa => {
    if (uvpaa && localStorage.getItem(`credId`)) {
      document
        .querySelector('#uvpa_available')
        .classList.remove('hidden');
    } else {
      form.classList.remove('hidden');
    }
  });        
} else {
  form.classList.remove('hidden');
}

Modulo di riserva al password

L'utente dovrebbe anche essere in grado di scegliere di accedere con una password.

Mostra il modulo per la password e nascondi il pulsante di autenticazione quando l'utente fa clic su Accedi con password.

views/reauth.html

const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
  form.classList.remove('hidden');
  document
    .querySelector('#uvpa_available')
    .classList.add('hidden');
});

c4a82800889f078c.png

Richiamare l'autenticazione biometrica

Infine, attiva l'autenticazione biometrica.

  1. Aggiungi authenticate all'istruzione import esistente:

views/reauth.html

import { _fetch, authenticate } from '/client.js';
  1. Richiama authenticate() quando l'utente tocca Autentica per avviare l'autenticazione biometrica.

Assicurati che l'errore di autenticazione biometrica torni nel modulo della password.

views/reauth.html

const button = document.querySelector('#reauth');
button.addEventListener('click', e => {
  authenticate().then(user => {
    if (user) {
      location.href = '/home';
    } else {
      throw 'User not found.';
    }
  }).catch(e => {
    console.error(e.message || e);
    alert('Authentication failed. Use password to sign-in.');
    form.classList.remove('hidden');
    document.querySelector('#uvpa_available').classList.add('hidden');
  });        
});

Codice finale per questa sezione

views/reauth.html

...
    <main class="content">
      <div id="uvpa_available" class="hidden">
        <h2>
          Verify your identity
        </h2>
        <div>
          <mwc-button id="reauth" raised>Authenticate</mwc-button>
        </div>
        <div>
          <mwc-button id="cancel">Sign-in with password</mwc-button>
        </div>
      </div>
      <form id="form" method="POST" action="/auth/password" class="hidden">
        <h2>
          Enter a password
        </h2>
        <input type="hidden" name="username" value="{{username}}" />
        <div class="mdc-text-field mdc-text-field--filled">
          <span class="mdc-text-field__ripple"></span>
          <label class="mdc-floating-label" id="password-label">password</label>
          <input type="password" class="mdc-text-field__input" aria-labelledby="password-label" name="password" />
          <span class="mdc-line-ripple"></span>
        </div>
        <input type="submit" class="mdc-button mdc-button--raised" value="Sign-In" />
        <p class="instructions">password will be ignored in this demo.</p>
      </form>
    </main>
    <script src="https://unpkg.com/material-components-web@7.0.0/dist/material-components-web.min.js"></script>
    <script type="module">
      new mdc.textField.MDCTextField(document.querySelector('.mdc-text-field'));
      import { _fetch, authenticate } from '/client.js';
      const form = document.querySelector('#form');
      form.addEventListener('submit', e => {
        e.preventDefault();
        const form = new FormData(e.target);
        const cred = {};
        form.forEach((v, k) => cred[k] = v);
        _fetch(e.target.action, cred)
        .then(user => {
          location.href = '/home';
        })
        .catch(e => alert(e));
      });

      if (window.PublicKeyCredential) {
        PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
        .then(uvpaa => {
          if (uvpaa && localStorage.getItem(`credId`)) {
            document
              .querySelector('#uvpa_available')
              .classList.remove('hidden');
          } else {
            form.classList.remove('hidden');
          }
        });        
      } else {
        form.classList.remove('hidden');
      }

      const cancel = document.querySelector('#cancel');
      cancel.addEventListener('click', e => {
        form.classList.remove('hidden');
        document
          .querySelector('#uvpa_available')
          .classList.add('hidden');
      });

      const button = document.querySelector('#reauth');
      button.addEventListener('click', e => {
        authenticate().then(user => {
          if (user) {
            location.href = '/home';
          } else {
            throw 'User not found.';
          }
        }).catch(e => {
          console.error(e.message || e);
          alert('Authentication failed. Use password to sign-in.');
          form.classList.remove('hidden');
          document.querySelector('#uvpa_available').classList.add('hidden');
        });        
      });
    </script>
...

7. Complimenti!

Hai terminato questo codelab!

Scopri di più

Un ringraziamento speciale a Yuriy Ackermann di FIDO Alliance per l'aiuto.