Crea una passkey per gli accessi senza password

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

L'utilizzo delle passkey migliora la sicurezza, semplifica gli accessi e sostituisce le password. A differenza delle password comuni, che gli utenti devono ricordare e inserire manualmente, le passkey utilizzano i meccanismi di blocco schermo del dispositivo, come i dati biometrici o i PIN, e riducono i rischi di phishing e il furto di credenziali.

Le passkey si sincronizzano tra i dispositivi utilizzando fornitori di passkey come Gestore delle password di Google e Portachiavi iCloud.

È necessario creare una passkey, memorizzando la chiave privata in modo sicuro nel fornitore della passkey insieme ai metadati necessari e alla relativa chiave pubblica memorizzata sul server per l'autenticazione. La chiave privata emette una firma dopo la verifica dell'utente sul dominio valido, rendendo le passkey resistenti al phishing. La chiave pubblica verifica la firma senza memorizzare le credenziali sensibili, rendendo le passkey resistenti al furto delle credenziali.

Come funziona la creazione di una passkey

Prima che un utente possa accedere con una passkey, devi crearla, associarla a un account utente e memorizzare la relativa chiave pubblica sul tuo server.

Potresti chiedere agli utenti di creare una passkey in una delle seguenti situazioni:

  • Durante o dopo la registrazione.
  • Dopo aver eseguito l'accesso.
  • Dopo aver eseguito l'accesso utilizzando una passkey da un altro dispositivo (ovvero [authenticatorAttachment](https://web.dev/articles/passkey-form-autofill#authenticator-attachment) è 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 delle passkey sono:

  • Backend: memorizza i dettagli dell'account utente, inclusa la chiave pubblica.
  • Frontend: comunica con il browser e recupera i dati necessari dal backend.
  • Browser: esegue il codice JavaScript e interagisce con l'API WebAuthn.
  • Fornitore di passkey: crea e memorizza la passkey. In genere si tratta di un gestore delle password come Gestore delle password di Google o di una chiave di sicurezza.
La procedura di creazione e registrazione di una passkey
La procedura di creazione e registrazione di una passkey.

Prima di creare una passkey, assicurati che il sistema soddisfi i seguenti prerequisiti:

  • L'account utente viene verificato tramite un metodo sicuro (ad esempio email, verifica telefonica o federazione dell'identità) in un breve periodo di tempo.

  • Il frontend e il backend possono comunicare in modo sicuro per scambiare i dati delle credenziali.

  • Il browser supporta WebAuthn e la creazione di passkey.

Nelle sezioni seguenti ti mostreremo come controllare la maggior parte di questi elementi.

Una volta che il sistema soddisfa queste condizioni, viene eseguita la seguente procedura per creare una passkey:

  1. Il sistema attiva la procedura di creazione della passkey quando l'utente avvia l'azione (ad esempio facendo clic su un pulsante "Crea una passkey" nella pagina di gestione delle passkey o dopo aver completato la registrazione).
  2. Il frontend richiede i dati necessari per le credenziali dal backend, tra cui informazioni sull'utente, una verifica e gli ID credenziale per evitare duplicati.
  3. Il frontend chiama navigator.credentials.create() per chiedere al fornitore di passkey del dispositivo di generare una passkey utilizzando le informazioni del backend. Tieni presente che questa chiamata restituisce una promessa.
  4. Il dispositivo dell'utente lo autentica utilizzando un metodo biometrico, un PIN o una sequenza per creare la passkey.
  5. Il provider di passkey crea una passkey e restituisce una credenziale della chiave pubblica al frontend, risolvendo la promessa.
  6. Il frontend invia la credenziale della chiave pubblica generata al backend.
  7. Il backend memorizza la chiave pubblica e altri dati importanti per la futura autenticazione.
  8. Il backend invia una notifica all'utente (ad esempio via email) per confermare la creazione della passkey e rilevare potenziali accessi non autorizzati.

Questa procedura garantisce una procedura di registrazione delle passkey sicura e senza problemi per gli utenti.

Compatibilità

La maggior parte dei browser supporta WebAuthn, con alcune piccole lacune. Visita la pagina passkeys.dev per informazioni dettagliate sulla compatibilità del browser e del sistema operativo.

Creare una nuova passkey

Per creare una nuova passkey, il frontend deve seguire questa procedura:

  1. Verifica la compatibilità.
  2. Recupero delle informazioni dal back-end.
  3. Chiama l'API WebAuth per creare una passkey.
  4. Invia la chiave pubblica restituita al backend.
  5. Salva la credenziale.

Le sezioni seguenti mostrano come procedere.

Verifica la compatibilità della tua macchina

Prima di mostrare un pulsante "Crea una nuova passkey", il frontend deve verificare se:

  • Il browser supporta WebAuthn con PublicKeyCredential.

Browser Support

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

Source

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

Browser Support

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

Source

Browser Support

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

Source

Lo snippet di codice seguente mostra come verificare la compatibilità prima di visualizzare le opzioni relative alla passkey.

// 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  
    }  
  });  
}  

In questo esempio, il pulsante Crea una nuova passkey deve essere visualizzato solo se sono soddisfatte tutte le condizioni.

Recuperare le informazioni dal backend

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

Il seguente snippet di codice mostra un oggetto JSON con le informazioni necessarie per chiamare navigator.credentials.create():

// Example `PublicKeyCredentialCreationOptions` contents
{
  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,
  }
}

Le coppie chiave-valore nell'oggetto contengono le seguenti informazioni:

  • challenge: una verifica generata dal server in ArrayBuffer per questa registrazione.
  • rp.id: un ID RP (Relying Party ID), un dominio e un sito web possono specificare il proprio dominio o un suffisso 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 di example.com. Per ulteriori informazioni, consulta Consentire il riutilizzo delle passkey sui tuoi siti con le richieste di origine correlate.
  • rp.name: il nome della RP (parte che si basa su). Questo valore è deprecato in WebAuthn L3, ma è incluso per motivi di compatibilità.
  • user.id: un ID utente univoco in ArrayBuffer, generato al momento della creazione dell'account. Deve essere permanente, a differenza di un nome utente che può essere modificato. L'ID utente identifica un account, ma non deve contenere informazioni che consentono l'identificazione personale (PII). È probabile che tu abbia già un ID utente nel tuo sistema, ma se necessario, creane uno specifico per le passkey per mantenerlo privo di PII.
  • user.name: un identificatore univoco per l'account che l'utente riconoscerà, come il suo indirizzo email o il suo nome utente. Verrà visualizzato nel selettore account.
  • user.displayName: un nome obbligatorio più facile da ricordare 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 potrebbe essere visualizzata nel selettore dell'account a seconda del browser.
  • pubKeyCredParams: specifica gli algoritmi delle chiavi pubbliche supportati dalla RP (parte interessata). 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.
  • excludeCredentials: un elenco di ID credenziali già registrati. Impedisce la registrazione dello stesso dispositivo due volte 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.
  • authenticatorSelection.authenticatorAttachment: imposta questo valore su "platform" insieme a hint: ['client-device'] 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 authenticator di piattaforma (un authenticator incorporato nel dispositivo della piattaforma) che non richiede, ad esempio, di inserire 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.
  • 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. Imposta questo valore su "preferred" o ometti la proprietà.

Ti consigliamo di creare l'oggetto sul server, di codificare l'ArrayBuffer con Base64URL e di recuperarlo dal frontend. In questo modo, puoi decodificare il payload utilizzando PublicKeyCredential.parseCreationOptionsFromJSON() e passarlo direttamente a navigator.credentials.create().

Il seguente snippet di codice mostra come recuperare e decodificare le informazioni necessarie per creare la passkey.

// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');

// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseCreationOptionsFromJSON(decoded_options);
...

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.

Browser Support

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

Source

// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
  publicKey: options
});

Invia la credenziale della chiave pubblica restituita al backend

Dopo la verifica dell'utente tramite 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. Il sito non deve trattarlo come un errore. L'utente voleva che il dispositivo locale fosse registrato e lo è.
  • NotAllowedError: l'utente ha annullato l'operazione.
  • AbortError: l'operazione è stata interrotta.
  • 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 codificato in Base64Url della passkey creata. Questo ID aiuta il browser a determinare se una passkey corrispondente è presente nel 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".

Codifica l'oggetto con il metodo .toJSON(), serializzalo con JSON.stringify() e poi invialo al server.

...

// Encode and serialize the `PublicKeyCredential`.
const _result = credential.toJSON();
const result = JSON.stringify(_result);

// Encode and send the credential to the server for verification.  
const response = await fetch('/webauthn/registerResponse', {
  method: 'post',
  credentials: 'same-origin',
  body: result
});
...

Salva la credenziale

Dopo aver ricevuto la credenziale della chiave pubblica sul backend, consigliamo di utilizzare una libreria o una soluzione lato server anziché scrivere il tuo codice per elaborare una credenziale della chiave pubblica.

Puoi quindi archiviare le informazioni recuperate dalla credenziale nel database per uso futuro.

Il seguente elenco include le proprietà consigliate da salvare:

  • ID credenziale: l'ID credenziale restituito con la credenziale della chiave pubblica.
  • Nome credenziale: il nome della credenziale. Assegna un nome al fornitore di passkey che l'ha creata, che può essere identificato in base all'AAGUID.
  • ID utente: l'ID utente utilizzato per creare la passkey.
  • Chiave pubblica: la chiave pubblica restituita con la credenziale della chiave pubblica. Questo è necessario per verificare un'affermazione della passkey.
  • Data e ora di creazione: registra la data e l'ora di creazione della passkey. Questo è utile per identificare la passkey.
  • Data e ora dell'ultimo utilizzo: registra la data e l'ora dell'ultimo utilizzo della passkey per accedere. Questo è utile per determinare quale passkey l'utente ha utilizzato (o meno).
  • AAGUID: un identificatore univoco del provider della passkey.
  • Backup Eligibility flag: true se il dispositivo è idoneo per la sincronizzazione delle passkey. Queste informazioni aiutano gli utenti a identificare le passkey sincronizzabili e le passkey associate al dispositivo (non sincronizzabili) nella pagina di gestione delle passkey.

Segui istruzioni più dettagliate alla pagina Registrazione della passkey lato server

Segnale se la registrazione non va a buon fine

Se la registrazione di una passkey non riesce, l'utente potrebbe essere confuso. Se nel fornitore di passkey è presente una passkey disponibile per l'utente, ma la chiave pubblica associata non è memorizzata lato server, i tentativi di accesso utilizzando la passkey non andranno mai a buon fine ed è difficile risolvere il problema. Assicurati di comunicarlo all'utente se è così.

Per evitare questa condizione, puoi segnalare una passkey sconosciuta al fornitore di passkey utilizzando l'API Signal. Chiamando PublicKeyCredential.signalUnknownCredential() con un ID RP e un ID credenza, l'RP può informare il fornitore della passkey che la credenza specificata è stata rimossa o non esiste. Spetta al fornitore della passkey gestire questo indicatore, ma se supportato, la passkey associata dovrebbe essere rimossa.

// Detect authentication failure due to lack of the credential
if (response.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

Per scoprire di più sull'API Signal, leggi Mantenere le passkey coerenti con le credenziali sul server con l'API Signal.

Invia una notifica all'utente

L'invio di una notifica (ad esempio un'email) quando viene registrata una passkey aiuta gli utenti a rilevare l'accesso non autorizzato all'account. Se un malintenzionato crea una passkey senza che l'utente lo sappia, la passkey rimane disponibile per usi impropri futuri, anche dopo la modifica della password. La notifica avvisa l'utente e aiuta a evitarlo.

Elenco di controllo

  • Verifica l'utente (preferibilmente tramite email o un metodo sicuro) prima di consentirgli di creare una passkey.
  • Evita di creare passkey duplicate per lo stesso provider di passkey utilizzando excludeCredentials.
  • Salva l'AAGUID per identificare il provider di passkey e assegnare un nome alla credenziale per l'utente.
  • Indica se un tentativo di registrazione di una passkey non va a buon fine con PublicKeyCredential.signalUnknownCredential().
  • Invia una notifica all'utente dopo aver creato e registrato una passkey per il suo account.

Risorse

Passaggio successivo: accedi con una passkey tramite la compilazione automatica dei moduli.