Panoramica
Ecco una panoramica generale dei passaggi chiave dell'autenticazione con passkey:

- Definisci la verifica e le altre opzioni necessarie per l'autenticazione con una passkey. Inviali al client, in modo da poterli passare alla chiamata di autenticazione con passkey (
navigator.credentials.getsul web). Dopo che l'utente conferma l'autenticazione con passkey, la chiamata di autenticazione con passkey viene risolta e restituisce una credenziale (PublicKeyCredential). La credenziale contiene un'asserzione di autenticazione.
- Verifica l'asserzione di autenticazione.
- Se l'asserzione di autenticazione è valida, autentica l'utente.
Le sezioni seguenti approfondiscono i dettagli di ogni passaggio.
Crea la sfida
In pratica, una verifica è un array di byte casuali, rappresentato come un oggetto ArrayBuffer.
// Example challenge, base64URL-encoded
weMLPOSx1VfSnMV6uPwDKbjGdKRMaUDGxeDEUTT5VN8
Per garantire che la challenge raggiunga il suo scopo, devi:
- Assicurati che la stessa verifica non venga mai utilizzata più di una volta. Genera una nuova verifica a ogni tentativo di accesso. Elimina la verifica dopo ogni tentativo di accesso, indipendentemente dal fatto che sia riuscito o meno. Scarta la verifica anche dopo un determinato periodo di tempo. Non accettare mai la stessa verifica in una risposta più di una volta.
- Assicurati che la verifica sia protetta tramite crittografia. Una verifica dovrebbe essere praticamente impossibile da indovinare. Per creare una verifica sicura dal punto di vista crittografico lato server, è meglio affidarsi a una libreria FIDO lato server di tua fiducia. Se invece crei le tue challenge, utilizza la funzionalità di crittografia integrata disponibile nel tuo stack tecnologico o cerca librerie progettate per casi d'uso di crittografia. Alcuni esempi sono iso-crypto in Node.js o secrets in Python. In base alla specifica, la verifica deve avere una lunghezza di almeno 16 byte per essere considerata sicura.
Una volta creata una verifica, salvala nella sessione dell'utente per verificarla in un secondo momento.
Crea opzioni di richiesta delle credenziali
Crea le opzioni di richiesta delle credenziali come oggetto publicKeyCredentialRequestOptions.
Per farlo, utilizza la libreria lato server FIDO. In genere, offre una funzione di utilità che può creare queste opzioni per te. SimpleWebAuthn offre, ad esempio, generateAuthenticationOptions.
publicKeyCredentialRequestOptions deve contenere tutte le informazioni necessarie per l'autenticazione con passkey. Passa queste informazioni alla funzione nella libreria lato server FIDO responsabile della creazione dell'oggetto publicKeyCredentialRequestOptions.
Alcuni campi di publicKeyCredentialRequestOptions possono essere costanti. Altri devono essere definiti dinamicamente sul server:
rpId: l'ID RP a cui prevedi che sia associata la credenziale, ad esempioexample.com. L'autenticazione andrà a buon fine solo se l'ID RP fornito qui corrisponde all'ID RP associato alla credenziale. Per compilare l'ID RP, utilizza lo stesso valore dell'ID RP impostato inpublicKeyCredentialCreationOptionsdurante la registrazione delle credenziali.challenge: un dato che il provider di passkey firmerà per dimostrare che l'utente è in possesso della passkey al momento della richiesta di autenticazione. Esamina i dettagli in Crea la verifica.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. Per maggiori dettagli, consulta Recupera una verifica dal server RP e Approfondimento sulle credenziali rilevabili.userVerification: indica se la verifica dell'utente tramite il blocco schermo del dispositivo è "obbligatoria", "preferita" o "sconsigliata". Consulta la sezione Recupera una verifica dal server RP.timeout: il tempo (in millisecondi) che l'utente può impiegare per completare l'autenticazione. Dovrebbe essere ragionevolmente generoso e più breve della durata dichallenge. Il valore predefinito consigliato è 5 minuti, ma puoi aumentarlo fino a 10 minuti, che rientra comunque nell'intervallo consigliato. I timeout lunghi sono utili se prevedi che gli utenti utilizzino il flusso di lavoro ibrido, che in genere richiede un po' più di tempo. Se l'operazione scade, verrà generato un erroreNotAllowedError.
Dopo aver creato publicKeyCredentialRequestOptions, invialo al cliente.
challenge la decodifica avviene lato client.Codice di esempio: crea le opzioni di richiesta delle credenziali
Nei nostri esempi utilizziamo la libreria SimpleWebAuthn. In questo caso, affidiamo la creazione delle opzioni di richiesta delle credenziali alla funzione generateAuthenticationOptions.
import {
generateAuthenticationOptions,
} from '@simplewebauthn/server';
router.post('/signinRequest', csrfCheck, async (req, res) => {
// Ensure you nest calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// Use the generateAuthenticationOptions function from SimpleWebAuthn
const options = await generateAuthenticationOptions({
rpID: process.env.HOSTNAME,
allowCredentials: [],
});
// Save the challenge in the user session
req.session.challenge = options.challenge;
return res.json(options);
} catch (e) {
console.error(e);
return res.status(400).json({ error: e.message });
}
});
Verifica e accedi all'utente
Quando navigator.credentials.get viene risolto correttamente sul client, restituisce un oggetto PublicKeyCredential.
navigator.credentials.get restituisce un PublicKeyCredential.response è un AuthenticatorAssertionResponse. Rappresenta la risposta del provider di passkey all'istruzione del client di creare ciò che è necessario per provare a eseguire l'autenticazione con una passkey sulla RP. Contiene:
response.authenticatorDataeresponse.clientDataJSON, come nel passaggio di registrazione della passkey.response.signature, che contiene una firma su questi valori.
Invia l'oggetto PublicKeyCredential al server.
Sul server, segui questi passaggi:
- Raccogli le informazioni necessarie per verificare l'asserzione e autenticare l'utente:
- Recupera la verifica prevista che hai memorizzato nella sessione quando hai generato le opzioni di autenticazione.
- Ottieni l'origine e l'ID RP previsti.
- Trova l'utente nel tuo database. Nel caso delle credenziali rilevabili, non sai chi è l'utente che effettua una richiesta di autenticazione. Per scoprirlo, hai due opzioni:
- Opzione 1: utilizza
response.userHandlenell'oggettoPublicKeyCredential. Nella tabella Utenti, cerca il valorepasskey_user_idche corrisponde auserHandle. - Opzione 2: utilizza la credenziale
idpresente nell'oggettoPublicKeyCredential. Nella tabella Credenziali della chiave pubblica, cerca la credenzialeidcorrispondente alla credenzialeidpresente nell'oggettoPublicKeyCredential. Quindi, cerca l'utente corrispondente utilizzando la chiave esternapasskey_user_idnella tabella Utenti.
- Opzione 1: utilizza
- Trova nel tuo database le informazioni sulle credenziali di chiave pubblica che corrispondono all'asserzione di autenticazione che hai ricevuto. Per farlo, nella tabella Credenziali della chiave pubblica, cerca la credenziale
idcorrispondente alla credenzialeidpresente nell'oggettoPublicKeyCredential.
Verifica l'asserzione di autenticazione. Affida questo passaggio di verifica alla tua libreria lato server FIDO, che in genere offre una funzione di utilità per questo scopo. SimpleWebAuthn offre, ad esempio,
verifyAuthenticationResponse. Scopri cosa succede dietro le quinte nell'Appendice: verifica della risposta di autenticazione.Elimina la verifica indipendentemente dal fatto che la verifica sia andata a buon fine o meno, per evitare attacchi di tipo replay.
Consenti l'accesso all'utente. Se la verifica è andata a buon fine, aggiorna le informazioni sulla sessione per contrassegnare l'utente come connesso. Potresti anche voler restituire un oggetto
useral client, in modo che il frontend possa utilizzare le informazioni associate all'utente appena autenticato.
Codice di esempio: verifica e accedi con l'utente
Nei nostri esempi utilizziamo la libreria SimpleWebAuthn. Qui, affidiamo la verifica della risposta di autenticazione alla funzione verifyAuthenticationResponse.
import {
verifyAuthenticationResponse,
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';
router.post('/signinResponse', csrfCheck, async (req, res) => {
const response = req.body;
const expectedChallenge = req.session.challenge;
const expectedOrigin = getOrigin(req.get('User-Agent'));
const expectedRPID = process.env.HOSTNAME;
// Ensure you nest verification function calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// Find the credential stored to the database by the credential ID
const cred = Credentials.findById(response.id);
if (!cred) {
throw new Error('Credential not found.');
}
// Find the user - Here alternatively we could look up the user directly
// in the Users table via userHandle
const user = Users.findByPasskeyUserId(cred.passkey_user_id);
if (!user) {
throw new Error('User not found.');
}
// Prepare credential information for verification
const credential = {
id: cred.id,
publicKey: isoBase64URL.toBuffer(cred.publicKey),
transports: cred.transports,
};
// Verify the credential
const { verified, authenticationInfo } = await verifyAuthenticationResponse({
response,
expectedChallenge,
expectedOrigin,
expectedRPID,
credential,
requireUserVerification: false,
});
if (!verified) {
throw new Error('User verification failed.');
}
// Kill the challenge for this session.
delete req.session.challenge;
req.session.username = user.username;
req.session['signed-in'] = 'yes';
return res.json(user);
} catch (e) {
delete req.session.challenge;
console.error(e);
return res.status(400).json({ error: e.message });
}
});
Appendice: verifica della risposta di autenticazione
La verifica della risposta di autenticazione consiste nei seguenti controlli:
- Assicurati che l'ID RP corrisponda al tuo sito.
- Assicurati che l'origine della richiesta corrisponda all'origine di accesso del tuo sito. Per le app per Android, consulta Verifica l'origine.
- Verifica che il dispositivo sia stato in grado di fornire la verifica che hai richiesto.
- Verifica che durante l'autenticazione l'utente abbia seguito i requisiti che hai imposto in qualità di RP. Se richiedi la verifica dell'utente, assicurati che il flag
uv(utente verificato) inauthenticatorDatasiatrue. Verifica che il flagup(utente presente) inauthenticatorDatasiatrue, poiché la presenza dell'utente è sempre obbligatoria per le passkey. - Verifica la firma. Per verificare la firma, devi avere:
- La firma, ovvero la richiesta di autenticazione firmata:
response.signature - La chiave pubblica con cui verificare la firma.
- I dati firmati originali. Questi sono i dati di cui verificare la firma.
- L'algoritmo crittografico utilizzato per creare la firma.
- La firma, ovvero la richiesta di autenticazione firmata:
Per scoprire di più su questi passaggi, consulta il codice sorgente di SimpleWebAuthn per verifyAuthenticationResponse o consulta l'elenco completo delle verifiche nella specifica.