Accedere con una passkey tramite la compilazione automatica dei moduli

Crea un'esperienza di accesso che sfrutti le passkey e consenta comunque di accedere agli utenti con password esistenti.

Le passkey sostituiscono le password e rendono gli account utente sul web più sicuri, semplici e facili da usare. Tuttavia, la transizione dall'autenticazione basata su password a quella basata su passkey può complicare l'esperienza utente. Usare la compilazione automatica dei moduli per suggerire passkey può contribuire a creare un'esperienza unificata.

Perché usare la compilazione automatica dei moduli per accedere con una passkey?

Con una passkey, un utente può accedere a un sito web semplicemente utilizzando l'impronta, il volto o il PIN del dispositivo.

Idealmente, non ci sarebbero utenti di password e il flusso di autenticazione potrebbe essere semplice come un singolo pulsante di accesso. Quando l'utente tocca il pulsante, viene visualizzata una finestra di dialogo del selettore account, in cui l'utente può scegliere un account, sbloccare lo schermo per eseguire la verifica e accedere.

Tuttavia, la transizione dall'autenticazione basata su password all'autenticazione basata su passkey può essere complicata. Man mano che gli utenti passano alle passkey, chi usa password e siti web dovrà comunque supportare entrambi i tipi di utenti. Gli utenti non dovrebbero ricordare su quali siti hanno eseguito il passaggio alle passkey, quindi chiedere agli utenti di selezionare in anticipo il metodo da utilizzare sarebbe una UX scadente.

Anche le passkey sono una nuova tecnologia. Spiegare i modelli e assicurarsi che gli utenti si sentano a proprio agio nel utilizzarli può essere complicato per i siti web. Possiamo contare su esperienze utente note per la compilazione automatica delle password, in modo da risolvere entrambi i problemi.

UI condizionale

Per creare un'esperienza utente efficiente sia per gli utenti di passkey sia per gli utenti con password, puoi includere le passkey nei suggerimenti di compilazione automatica. Questa è chiamata UI condizionale e fa parte dello standard WebAuthn.

Non appena l'utente tocca il campo di immissione del nome utente, viene visualizzata una finestra di dialogo con i suggerimenti di compilazione automatica che evidenzia le passkey memorizzate insieme ai suggerimenti di compilazione automatica delle password. L'utente può quindi scegliere un account e utilizzare il blocco schermo del dispositivo per accedere.

In questo modo, gli utenti possono accedere al tuo sito web con il modulo esistente come se non fosse cambiato nulla, ma con l'ulteriore vantaggio di sicurezza delle passkey, se ne hanno una.

Come funziona

Per eseguire l'autenticazione con una passkey, utilizza l'API WebAuthn.

I quattro componenti di un flusso di autenticazione passkey sono: l'utente:

  • Backend: il tuo server di backend che contiene il database degli account in cui sono archiviati la chiave pubblica e altri metadati relativi alla passkey.
  • Frontend: il frontend che comunica con il browser e invia richieste di recupero al backend.
  • Browser: il browser dell'utente su cui è in esecuzione JavaScript.
  • Authenticator: l'autenticatore dell'utente che crea e memorizza la passkey. che potrebbe essere sullo stesso dispositivo del browser (ad es. se usi Windows Hello) o su un altro dispositivo, ad esempio uno smartphone.
Diagramma di autenticazione con passkey
  1. Non appena un utente arriva al frontend, richiede al backend di autenticarsi con una passkey e chiama navigator.credentials.get() per avviare l'autenticazione con una passkey. Viene restituito un Promise.
  2. Quando l'utente posiziona il cursore nel campo di accesso, il browser mostra una finestra di dialogo di compilazione automatica delle password che include le passkey. Se l'utente seleziona una passkey, viene visualizzata una finestra di dialogo di autenticazione.
  3. Dopo che l'utente ha verificato la propria identità utilizzando il blocco schermo del dispositivo, la promessa viene risolta e la credenziale di una chiave pubblica viene restituita al frontend.
  4. Il frontend invia la credenziale della chiave pubblica al backend. Il backend verifica la firma in base alla chiave pubblica dell'account corrispondente nel database. Se l'operazione riesce, l'utente accede.

Prerequisiti

La UI condizionale WebAuthn è supportata pubblicamente in Safari su iOS 16, iPadOS 16 e macOS Ventura. È disponibile anche su Chrome su Android, macOS e Windows 11 22H2.

Autenticazione con una passkey tramite la compilazione automatica dei moduli

Quando un utente vuole accedere, puoi effettuare una chiamata get condizionale WebAuthn per indicare che le passkey potrebbero essere incluse nei suggerimenti di compilazione automatica. Una chiamata condizionale all'API navigator.credentials.get() di WebAuthn non mostra l'interfaccia utente e rimane in attesa finché l'utente non sceglie un account con cui accedere dai suggerimenti di compilazione automatica. Se l'utente sceglie una passkey, il browser risolve la promessa con una credenziale anziché compilare il modulo di accesso. Pertanto, è responsabilità della pagina far eseguire l'accesso all'utente.

Annota campo di immissione del modulo

Se necessario, aggiungi un attributo autocomplete al campo input del nome utente. Aggiungi username e webauthn come token per consentire al sistema di suggerire passkey.

<input type="text" name="username" autocomplete="username webauthn" ...>

Rilevamento delle caratteristiche

Prima di richiamare una chiamata API WebAuthn condizionale, controlla se:

  • Il browser supporta WebAuthn.
  • Il browser supporta l'interfaccia utente condizionale WebAuthn.
// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if conditional mediation is available.  
  const isCMA = await PublicKeyCredential.​​isConditionalMediationAvailable();  
  if (isCMA) {  
    // Call WebAuthn authentication  
  }  
}  

Recupera una verifica dal server RP

Recupera dal server RP una verifica necessaria per chiamare navigator.credentials.get():

  • challenge: una challenge generata dal server in un Arraybu. Questa operazione è necessaria per evitare gli attacchi ripetuti. Assicurati di generare una nuova verifica a ogni tentativo di accesso e ignorala dopo un determinato periodo di tempo o se un tentativo di accesso non viene convalidato. Consideralo come un token CSRF.
  • allowCredentials: un array di credenziali accettabili per questa autenticazione. Passa un array vuoto per consentire all'utente di selezionare una passkey disponibile da un elenco mostrato dal browser.
  • userVerification: Indica se la verifica utente tramite il blocco schermo del dispositivo è "required", "preferred" o "discouraged". Il valore predefinito è "preferred", il che significa che l'autenticatore potrebbe saltare la verifica dell'utente. Impostalo su "preferred" oppure ometti la proprietà.

Chiama l'API WebAuthn con il flag conditional per autenticare l'utente

Chiama navigator.credentials.get() per iniziare ad attendere l'autenticazione utente.

// To abort a WebAuthn call, instantiate an `AbortController`.
const abortController = new AbortController();

const publicKeyCredentialRequestOptions = {
  // Server generated challenge
  challenge: ****,
  // The same RP ID as used during registration
  rpId: 'example.com',
};

const credential = await navigator.credentials.get({
  publicKey: publicKeyCredentialRequestOptions,
  signal: abortController.signal,
  // Specify 'conditional' to activate conditional UI
  mediation: 'conditional'
});
  • rpId: un ID RP è un dominio e un sito web può specificare il proprio dominio o un suffisso registrabile. Questo valore deve corrispondere al valore rp.id utilizzato quando è stata creata la passkey.

Ricordati di specificare mediation: 'conditional' per rendere la richiesta condizionale.

Invia la credenziale della chiave pubblica restituita al server RP

Dopo che l'utente ha selezionato un account e acconsentito utilizzando il blocco schermo del dispositivo, la promessa viene risolta restituisce un oggetto PublicKeyCredential al frontend della parte soggetta a limitazioni.

Una promessa può essere rifiutata per diversi motivi. Devi gestire gli errori di conseguenza, a seconda della proprietà name dell'oggetto Error:

  • NotAllowedError: l'utente ha annullato l'operazione.
  • Altre eccezioni: si è verificato un problema imprevisto. Il browser mostra all'utente una finestra di dialogo di errore.

L'oggetto credenziale della chiave pubblica contiene le seguenti proprietà:

  • id: l'ID codificato in base64url della credenziale passkey autenticata.
  • rawId: una versione Arraybu dell'ID credenziale.
  • response.clientDataJSON: un array di dati client. Questo campo contiene informazioni come la verifica e l'origine che il server RP dovrà verificare.
  • response.authenticatorData: un array di dati dell'autenticatore. Questo campo contiene informazioni come l'ID parte soggetta a limitazioni.
  • response.signature: un arrayBuffer della firma. Questo valore è il nucleo della credenziale e deve essere verificato sul server.
  • response.userHandle: un array buffer che conteneva l'ID utente impostato al momento della creazione. Questo valore può essere utilizzato, al posto dell'ID credenziale, se il server deve scegliere i valori ID che utilizza o se il backend vuole evitare di creare un indice sugli ID credenziali.
  • authenticatorAttachment: restituisce platform quando la credenziale proviene dal dispositivo locale. Altrimenti, cross-platform, in particolare se l'utente ha utilizzato un telefono per accedere. Se l'utente ha bisogno di utilizzare uno smartphone per accedere, valuta la possibilità di chiedergli di creare una passkey sul dispositivo locale.
  • type: questo campo è sempre impostato su "public-key".

Se utilizzi una libreria per gestire l'oggetto credenziali della chiave pubblica sul server RP, ti consigliamo di inviare l'intero oggetto al server dopo averlo codificato parzialmente con base64url.

Verifica la firma

Quando ricevi la credenziale della chiave pubblica sul server, passala alla libreria FIDO per elaborare l'oggetto.

Cerca l'ID credenziale corrispondente con la proprietà id (se devi determinare l'account utente, utilizza la proprietà userHandle, ovvero il user.id specificato durante la creazione della credenziale). Controlla se l'indirizzo signature della credenziale può essere verificato con la chiave pubblica archiviata. Per farlo, ti consigliamo di utilizzare una libreria lato server o una soluzione invece di scrivere il tuo codice. Puoi trovare librerie open source nel repository GitHub fantas-webauth.

Una volta verificata la credenziale con una chiave pubblica corrispondente, esegui l'accesso dell'utente.

Risorse