Crea una passkey per gli accessi senza password

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

L'utilizzo di passkey al posto delle password è un ottimo modo per i siti web di rendere i loro account utente più sicuri, semplici, facili da usare e senza password. Con una passkey è possibile accedere a un sito web o a un'app semplicemente utilizzando l'impronta, il PIN del volto o del dispositivo.

Prima che un utente possa accedere con una passkey, è necessario creare una passkey, associata a un account utente e memorizzare la relativa chiave pubblica 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 (ossia, authenticatorAttachment è cross-platform).
  • In una pagina dedicata in cui gli utenti possono gestire le proprie passkey.

Per creare una passkey, usa l'API WebAuthn.

I quattro componenti del flusso di registrazione delle passkey sono:

  • Backend: il server di backend che contiene il database degli account in cui è archiviata 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 JavaScript.
  • Authenticator: l'autenticatore dell'utente che crea e archivia la passkey. Potrebbe trovarsi sullo stesso dispositivo del browser (ad esempio quando utilizzi Windows Hello) o su un altro dispositivo, ad esempio uno smartphone.
Diagramma di registrazione delle passkey

Ecco la procedura per aggiungere una nuova passkey a un account utente esistente:

  1. Un utente accede al sito web.
  2. Dopo aver 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 utente, una verifica e gli ID credenziali 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 usando il blocco schermo del dispositivo. La promessa viene risolta e una credenziale di chiave pubblica viene restituita al frontend.
  6. Il frontend invia la credenziale della chiave pubblica al backend e archivia l'ID credenziali e la chiave pubblica associata all'account utente per le autenticazioni future.

Compatibilità

WebAuthn è supportato dalla maggior parte dei browser, ma presentano piccoli vuoti. Consulta Supporto dispositivi - passkeys.dev per scoprire quale combinazione di browser e sistema operativo supporta la creazione di una passkey.

Crea una nuova passkey

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

Rilevamento delle funzionalità

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

  • Il browser supporta WebAuthn.
  • Il dispositivo supporta un autenticatore della piattaforma (può creare una passkey e eseguire l'autenticazione con la passkey).
  • Il browser supporta l'interfaccia utente condizionale WebAuthn.
// 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 a quel momento, il pulsante "Crea una nuova passkey" non dovrebbe essere visualizzato.

Recupera informazioni importanti dal backend

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

  • challenge: un test generato dal server in ArrayBuffer per questa registrazione. Questo campo è obbligatorio ma non viene utilizzato durante la registrazione, a meno che non venga eseguita l'attestazione, un argomento avanzato non trattato qui.
  • user.id: 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 casuale di 16 byte generato per account andrà bene.
  • user.name: questo campo deve contenere un identificatore univoco per l'account che l'utente riconosce, ad esempio l'indirizzo email o il nome utente. Verrà visualizzato nel selettore degli account. Se utilizzi un nome utente, usa lo stesso valore dell'autenticazione della password.
  • user.displayName: questo campo è un nome obbligatorio e più facile da usare per l'account. Non deve essere necessariamente univoco e potrebbe essere il nome scelto dall'utente. Se il sito non ha un valore adatto da includere qui, trasmetti una stringa vuota. Potrebbe essere visualizzato sul selettore dell'account, a seconda del browser.
  • excludeCredentials: impedisce la registrazione dello stesso dispositivo fornendo un elenco di ID credenziali già registrati. Il membro 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.

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 illustrati sopra sono:

  • rp.id: Un ID RP è un dominio e un sito web può specificare il proprio dominio o un suffisso registrabile. Ad esempio, se l'origine di una parte soggetta a limitazioni è https://login.example.com:1337, l'ID della parte soggetta a limitazioni può essere login.example.com o example.com. Se l'ID della parte soggetta a limitazioni è specificato come example.com, l'utente può eseguire l'autenticazione su login.example.com o su qualsiasi sottodominio su example.com.

  • rp.name: il nome della parte soggetta a limitazioni.

  • pubKeyCredParams: questo campo specifica gli algoritmi a chiave pubblica supportati dalla parte soggetta a limitazioni. Ti consigliamo di impostarla su [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. Specifica il supporto per ECDSA con P-256 e RSA PKCS#1 e il relativo supporto offre una copertura completa.

  • authenticatorSelection.authenticatorAttachment: imposta questa opzione su "platform" se la creazione di questa passkey è un upgrade di una password, ad esempio in una promozione dopo un accesso. "platform" indica che la parte soggetta a limitazioni richiede un autenticatore della piattaforma (un autenticatore incorporato nel dispositivo della piattaforma) che non richieda di inserire, ad esempio, un token di sicurezza USB. L'utente ha un'opzione più semplice per creare una passkey.

  • authenticatorSelection.requireResidentKey: imposta il valore booleano "true". Una credenziale rilevabile (chiave residente) memorizza le informazioni utente nella passkey e consente agli utenti di selezionare l'account al momento dell'autenticazione.

  • authenticatorSelection.userVerification: indica se la verifica di un 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. Imposta questo elemento su "preferred" oppure ometti la proprietà.

Invia la credenziale della chiave pubblica restituita al backend

Dopo che l'utente acconsente a utilizzare 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: una passkey esiste già sul dispositivo. All'utente non verrà mostrata alcuna finestra di dialogo di errore e il sito non dovrebbe considerare questo come un errore: l'utente voleva registrare il dispositivo locale 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 delle credenziali di chiave pubblica contiene le seguenti proprietà:

  • id: un ID codificato in Base64URL della passkey creata. Questo ID aiuta il browser a determinare se nel dispositivo è presente una passkey corrispondente al momento dell'autenticazione. Questo valore deve essere archiviato nel database sul backend.
  • rawId: una versione ArrayBuffer dell'ID credenziale.
  • response.clientDataJSON: un dati client codificati da ArrayBuffer.
  • response.attestationObject: un oggetto attestazione codificato Arraybu. Contiene informazioni importanti come l'ID della parte soggetta a limitazioni, i flag e una chiave pubblica.
  • authenticatorAttachment: restituisce "platform" quando questa credenziale viene creata su un dispositivo che supporta una passkey.
  • type: questo campo è sempre impostato su "public-key".

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

Salva la credenziale

Dopo aver ricevuto la credenziale di chiave pubblica nel backend, passala alla libreria FIDO per elaborare l'oggetto.

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

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

La credenziale di chiave pubblica include anche le seguenti informazioni che puoi salvare nel database:

Per autenticare l'utente, leggi Accedere con una passkey tramite la compilazione automatica dei moduli.

Risorse