Crea una passkey per gli accessi senza password

Le passkey rendono gli account utente più sicuri, semplici e facili da usare.

L'utilizzo di passkey anziché di password è un ottimo modo per i siti web di rendere i propri account utente più sicuri, più semplici, più facili da usare e senza password. Con una passkey, un utente può accedere a un sito web o a un'app semplicemente utilizzando la propria impronta, il proprio volto o il PIN del dispositivo.

Prima che un utente possa accedere con una passkey, è necessario che questa venga creata, associata a un account utente e che la relativa chiave pubblica venga memorizzata sul server.

Come funziona

A un utente può essere chiesto di creare una passkey in una delle seguenti situazioni:

  • Quando un utente accede utilizzando una password.
  • Quando un utente accede utilizzando una passkey da un altro dispositivo (ovvero authenticatorAttachment è cross-platform).
  • In una pagina dedicata in cui gli utenti possono gestire le proprie passkey.

Per creare una passkey, utilizza l'API WebAuthn.

I quattro componenti del flusso di registrazione della passkey sono:

  • Backend: il server di backend che contiene il database degli account che memorizza la chiave pubblica e altri metadati sulla 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 il codice JavaScript.
  • Authenticator: l'app di autenticazione dell'utente che crea e memorizza la passkey. Potrebbe essere incluso il gestore delle password sullo stesso dispositivo del browser (ad esempio quando utilizzi Windows Hello) o su un altro dispositivo, come uno smartphone.
Diagramma di registrazione della passkey

La procedura per aggiungere una nuova passkey a un account utente esistente è la seguente:

  1. Un utente accede al sito web.
  2. Una volta eseguito l'accesso, l'utente richiede di creare una passkey sul frontend, ad esempio premendo un pulsante "Crea una passkey".
  3. Il frontend richiede informazioni al backend per creare una passkey, ad esempio informazioni sull'utente, una verifica e gli ID credenziale da escludere.
  4. Il frontend chiama navigator.credentials.create() per creare una passkey. Questa chiamata restituisce una promessa.
  5. Una passkey viene creata dopo che l'utente ha dato il consenso utilizzando il blocco schermo del dispositivo. La promessa viene risolta e una credenziale della chiave pubblica viene restituita al frontend.
  6. Il frontend invia la credenziale con chiave pubblica al backend e memorizza l'ID della credenziale e la chiave pubblica associata all'account utente per le autenticazioni future.

Compatibilità

WebAuthn è supportato dalla maggior parte dei browser, ma ci sono piccole lacune. Fai riferimento a Assistenza dispositivi - passkeys.dev per scoprire quale combinazione di browser e sistemi operativi supporta la creazione di una passkey.

Creare una nuova passkey

Ecco come dovrebbe funzionare un frontend su una richiesta di creazione di una nuova passkey.

Rilevamento di funzionalità

Prima di visualizzare un pulsante "Crea una nuova passkey", controlla se:

  • Il browser supporta WebAuthn con PublicKeyCredential.

Supporto dei browser

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Origine

  • Il dispositivo supporta un'autenticazione della piattaforma (può creare una passkey e autenticharsi con la passkey) conPublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable().

Supporto dei browser

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Origine

Supporto dei browser

  • Chrome: 108.
  • Edge: 108.
  • Firefox: 119.
  • Safari: 16.

Origine

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.  
// `​​isConditionalMediationAvailable` means the feature detection is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if user verifying platform authenticator is available.  
  Promise.all([  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),  
    PublicKeyCredential.​​isConditionalMediationAvailable(),  
  ]).then(results => {  
    if (results.every(r => r === true)) {  
      // Display "Create a new passkey" button  
    }  
  });  
}  

Finché non saranno soddisfatte tutte le condizioni, le passkey non saranno supportate su questo browser. Fino ad allora, il pulsante "Crea una nuova passkey" non dovrebbe essere visualizzato.

Recuperare informazioni importanti dal backend

Quando l'utente fa clic sul pulsante, recupera le informazioni importanti per chiamarenavigator.credentials.create() dal backend:

  • challenge: una verifica generata dal server in ArrayBuffer per questa registrazione. Questo valore è obbligatorio, ma non viene utilizzato durante la registrazione, a meno che non venga eseguita l'attestazione, un argomento avanzato non trattato qui.
  • user.id: l'ID univoco di un utente. Questo valore deve essere un ArrayBuffer che non include informazioni che consentono l'identificazione personale, ad esempio indirizzi email o nomi utente. Un valore random di 16 byte generato per account andrà bene.
  • user.name: in questo campo deve essere inserito un identificatore univoco per l'account che l'utente riconoscerà, come il suo indirizzo email o il suo nome utente. Verrà visualizzato nel selettore dell'account. Se utilizzi un nome utente, utilizza lo stesso valore dell'autenticazione con password.
  • user.displayName: questo campo è un nome obbligatorio e più intuitivo per l'account. Non deve essere univoco e potrebbe essere il nome scelto dall'utente. Se il tuo sito non ha un valore adatto da includere qui, passa una stringa vuota. Questa opzione può essere visualizzata nel selettore account a seconda del browser.
  • excludeCredentials: impedisce di registrare lo stesso dispositivo fornendo un elenco di ID credenziali già registrati. L'elemento transports se fornito, deve contenere il risultato della chiamata getTransports() durante la registrazione di ogni credenziale.

Chiama l'API WebAuthn per creare una passkey

Chiama navigator.credentials.create() per creare una nuova passkey. L'API restituisce una promessa, in attesa dell'interazione dell'utente che mostra una finestra di dialogo modale.

Supporto dei browser

  • Chrome: 60.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Origine

const publicKeyCredentialCreationOptions = {
  challenge: *****,
  rp: {
    name: "Example",
    id: "example.com",
  },
  user: {
    id: *****,
    name: "john78",
    displayName: "John",
  },
  pubKeyCredParams: [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}],
  excludeCredentials: [{
    id: *****,
    type: 'public-key',
    transports: ['internal'],
  }],
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
  }
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions
});

// Encode and send the credential to the server for verification.  

I parametri non spiegati sopra sono:

  • rp.id: un ID RP è un dominio e un sito web può specificare il proprio dominio o un sufisso registrabile. Ad esempio, se l'origine di un RP è https://login.example.com:1337, l'ID RP può essere login.example.com o example.com. Se l'ID RP è specificato come example.com, l'utente può autenticarsi su login.example.com o su qualsiasi sottodominio su example.com.

  • rp.name: il nome della RP.

  • pubKeyCredParams: Questo campo specifica gli algoritmi delle chiavi pubbliche supportati dall'RP. Ti consigliamo di impostarlo su [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. Questo specifica il supporto di ECDSA con P-256 e RSA PKCS#1 e il loro supporto offre una copertura completa.

  • authenticatorSelection.authenticatorAttachment: imposta questo valore su "platform" se la creazione della passkey è un upgrade da una password, ad esempio in una promozione dopo un accesso. "platform" indica che l'RP vuole un autenticatore di piattaforma (un autenticatore integrato nel dispositivo della piattaforma) che non richiede l'inserimento, ad esempio, di un token di sicurezza USB. L'utente ha un'opzione più semplice per creare una passkey.

  • authenticatorSelection.requireResidentKey: impostalo su un valore booleano "true". Una credenziale rilevabile (chiave residente) memorizza le informazioni dell'utente nella passkey e consente agli utenti di selezionare l'account al momento dell'autenticazione. Scopri di più sulle credenziali rilevabili nella pagina Approfondimento sulle credenziali rilevabili

  • authenticatorSelection.userVerification: indica se la verifica dell'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" o ometti la proprietà.

Invia la credenziale della chiave pubblica restituita al backend

Dopo che l'utente ha dato il consenso utilizzando il blocco schermo del dispositivo, viene creata una passkey e la promessa viene risolta restituendo un oggetto PublicKeyCredential al frontend.

La promessa può essere rifiutata per diversi motivi. Puoi gestire questi errori controllando la proprietà name dell'oggetto Error:

  • InvalidStateError: sul dispositivo esiste già una passkey. All'utente non verrà mostrata alcuna finestra di dialogo di errore e il sito non deve trattare questo come un errore: l'utente voleva che il dispositivo locale fosse registrato e lo è.
  • 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 della credenziale della chiave pubblica contiene le seguenti proprietà:

  • id: un ID della passkey creata codificato in Base64URL. Questo ID aiuta il browser a determinare se una passkey corrispondente è presente sul dispositivo al momento dell'autenticazione. Questo valore deve essere memorizzato nel database sul backend.
  • rawId: una versione ArrayBuffer dell'ID credenziale.
  • response.clientDataJSON: Dati del cliente codificati in ArrayBuffer.
  • response.attestationObject: un oggetto di attestazione codificato in ArrayBuffer. Questo contiene informazioni importanti come un ID RP, flag e una chiave pubblica.
  • authenticatorAttachment: restituisce "platform" quando questa credenziale viene creata su un dispositivo con passkey.
  • type: questo campo è sempre impostato su "public-key".

Se utilizzi una libreria per gestire l'oggetto della credenziale della chiave pubblica nel backend, consigliamo di inviare l'intero oggetto al backend dopo averlo codificato parzialmente con base64url.

Salva la credenziale

Una volta ricevuta la credenziale della chiave pubblica sul backend, passala alla biblioteca FIDO per elaborare l'oggetto.

Puoi quindi archiviare le informazioni recuperate dalla credenziale nel database per uso futuro. L'elenco seguente include alcune proprietà tipiche da salvare:

  • ID credenziale (chiave primaria)
  • User-ID
  • Chiave pubblica

La credenziale della chiave pubblica include anche le seguenti informazioni che potresti voler salvare nel database:

Segui istruzioni più dettagliate nella pagina Registrazione delle passkey lato server

Per autenticare l'utente, leggi l'articolo Accedere con una passkey tramite il completamento automatico dei moduli.

Risorse