1. Prima di iniziare
Le soluzioni di autenticazione tradizionali presentano una serie di sfide in termini di sicurezza e usabilità.
Le password sono ampiamente utilizzate, ma…
- Facili da dimenticare
- Gli utenti devono avere le conoscenze necessarie per creare password efficaci.
- Facili da usare per il phishing, la raccolta e la riproduzione da parte degli autori degli attacchi.
Android ha lavorato alla creazione dell'API Credential Manager per semplificare l'esperienza di accesso e affrontare i rischi per la sicurezza supportando le passkey, lo standard di settore di nuova generazione per l'autenticazione senza password.
Gestore delle credenziali riunisce il supporto per le passkey e lo combina con i metodi di autenticazione tradizionali, come password, Accedi con Google e così via.
Gli utenti potranno creare passkey, memorizzarle in Gestore delle password di Google, che le sincronizzerà sui dispositivi Android su cui l'utente ha eseguito l'accesso. Prima che un utente possa accedere con una passkey, questa deve essere creata, associata a un account utente e la relativa chiave pubblica deve essere memorizzata su un server.
In questo codelab, imparerai a registrarti utilizzando le passkey e la password tramite l'API Credential Manager e a utilizzarle per l'autenticazione futura. Esistono due flussi, tra cui:
- Registrazione : utilizzando passkey e password.
- Accedi : utilizzando le passkey e la password salvata.
Prerequisiti
- Conoscenza di base di come eseguire le app in Android Studio.
- Conoscenza di base del flusso di autenticazione nelle app per Android.
- Conoscenza di base delle passkey.
Obiettivi didattici
- Come creare una passkey.
- Come salvare la password nel Gestore delle password.
- Come autenticare gli utenti con una passkey o una password salvata.
Che cosa ti serve
Una delle seguenti combinazioni di dispositivi:
- Un dispositivo Android con Android 9 o versioni successive (per le passkey) e Android 4.4 o versioni successive(per l'autenticazione con password tramite l'API Credential Manager).
- Dispositivo preferibilmente con un sensore biometrico.
- Assicurati di registrare un blocco schermo (biometrico o di altro tipo).
- Versione del plug-in Kotlin : 1.8.10
2. Configurazione
Questa app di esempio richiede un collegamento delle risorse digitali a un sito web affinché Credential Manager possa convalidare il collegamento e procedere ulteriormente, quindi l'ID rp utilizzato nelle risposte simulate proviene da un server di terze parti simulato. Se vuoi provare la tua risposta simulata, prova ad aggiungere il dominio dell'app e non dimenticare di completare il collegamento delle risorse digitali come indicato qui.
Utilizza lo stesso debug.keystore menzionato nel progetto per creare varianti di debug e release per verificare il collegamento delle risorse digitali del nome pacchetto e di SHA sul server di test. (Questo passaggio è già stato eseguito per te per l'app di esempio in build.gradle).
- Clona questo repository sul tuo laptop dal ramo credman_codelab: https://github.com/android/identity-samples/tree/credman_codelab
git clone -b credman_codelab https://github.com/android/identity-samples.git
- Vai al modulo CredentialManager e apri il progetto in Android Studio.
Vediamo lo stato iniziale dell'app
Per vedere come funziona lo stato iniziale dell'app, segui questi passaggi:
- Avvia l'app.
- Viene visualizzata una schermata principale con un pulsante di registrazione e accesso. Questi pulsanti non fanno ancora nulla, ma ne abiliteremo la funzionalità nelle sezioni successive.

3. Aggiungere la possibilità di registrarsi utilizzando le passkey
Quando si registra un nuovo account su un'app per Android che utilizza l'API Credential Manager, l'utente può creare una passkey per il proprio account. Questa passkey verrà archiviata in modo sicuro nel provider di credenziali scelto dall'utente e utilizzata per gli accessi futuri, senza richiedere all'utente di inserire la password ogni volta.
Ora creerai una passkey e registrerai le credenziali utente utilizzando la biometria/il blocco schermo.
Registrarsi con una passkey
Il codice all'interno di CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignUpScreen.kt definisce un campo di testo "username" e un pulsante per registrarsi con una passkey.

Definisci la lambda createCredential() da utilizzare nei modelli di visualizzazione
Gli oggetti Credential Manager richiedono il passaggio di un Activity, associato a una schermata. Tuttavia, le operazioni di Credential Manager vengono in genere attivate in View Models e non è consigliabile fare riferimento alle attività all'interno di View Models. Pertanto, definiamo le funzioni di Credential Manager in un file separato CredentialManagerUtil.kt e le facciamo riferimento nelle schermate appropriate, che poi le passano ai relativi View Model come callback tramite funzioni lambda.
Individua il commento TODO nella funzione createCredential() in CredentialManagerUtil.kt e chiama la funzione CredentialManager.create():
CredentialManagerUtil.kt
suspend fun createCredential(
activity: Activity,
request: CreateCredentialRequest
): CreateCredentialResponse {
TODO("Create a CredentialManager object and call createCredential() with a CreateCredentialRequest")
val credentialManager = CredentialManager.create(activity)
return credentialManager.createCredential(activity, request)
}
Passa la sfida e l'altra risposta JSON a una chiamata createPasskey()
Prima di creare una passkey, devi richiedere al server le informazioni necessarie da trasmettere all'API Credential Manager durante la chiamata createCredential().
Hai già una risposta simulata negli asset del tuo progetto, chiamata RegFromServer.txt, che restituisce i parametri necessari in questo codelab.
- Nella tua app, vai a
SignUpViewModel.kt. Trova il metodosignUpWithPasskeysin cui scriverai la logica per creare una passkey e consentire l'accesso all'utente. Puoi trovare il metodo nella stessa classe. - Individua il blocco di commenti
TODOincreate a CreatePublicKeyCredentialRequest()e sostituiscilo con il seguente codice:
SignUpViewModel.kt
TODO("Create a CreatePublicKeyCredentialRequest() with necessary registration json from server")
val request = CreatePublicKeyCredentialRequest(
jsonProvider.fetchRegistrationJson()
.replace("<userId>", getEncodedUserId())
.replace("<userName>", _username.value)
.replace("<userDisplayName>", _username.value)
.replace("<challenge>", getEncodedChallenge())
)
Il metodo jsonProvider.fetchRegistrationJsonFromServer() legge una risposta JSON PublicKeyCredentialCreationOptions del server emulato dagli asset e restituisce il JSON di registrazione da trasmettere durante la creazione della passkey. Sostituiamo alcuni valori dei segnaposto con le voci degli utenti della nostra app e alcuni campi simulati:
- Questo JSON è incompleto e contiene 4 campi che devono essere sostituiti.
- L'ID utente deve essere univoco in modo che un utente possa creare più passkey (se necessario). Sostituisci
<userId>con il valoreuserIdgenerato. - Anche
<challenge>deve essere univoco, quindi genererai una sfida univoca casuale. Il metodo è già presente nel tuo codice.
Una risposta del server reale PublicKeyCredentialCreationOptions potrebbe restituire più opzioni. Di seguito è riportato un esempio di alcuni di questi campi:
{
"challenge": String,
"rp": {
"name": String,
"id": String
},
"user": {
"id": String,
"name": String,
"displayName": String
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
La tabella seguente illustra alcuni dei parametri importanti di un oggetto PublicKeyCredentialCreationOptions:
Parametri | Descrizioni |
Una stringa casuale generata dal server che contiene entropia sufficiente per renderne impossibile l'individuazione. Deve avere una lunghezza di almeno 16 byte. Questo campo è obbligatorio, ma non viene utilizzato durante la registrazione, a meno che non venga eseguita l'attestazione. | |
L'ID univoco di un utente. Questo valore non deve includere informazioni che consentono l'identificazione personale, ad esempio indirizzi email o nomi utente. Un valore casuale di 16 byte generato per account funzionerà bene. | |
Questo campo deve contenere un identificatore univoco per l'account che l'utente riconoscerà, ad esempio il suo indirizzo email o nome utente. Verrà visualizzato nel selettore account. Se utilizzi un nome utente, utilizza lo stesso valore dell'autenticazione con password. | |
Questo campo è un nome facoltativo e più intuitivo per l'account. | |
L'entità Relying Party corrisponde ai dettagli della tua applicazione. Ha i seguenti attributi:
| |
Elenco degli algoritmi e dei tipi di chiavi consentiti. Questo elenco deve contenere almeno un elemento. | |
L'utente che tenta di registrare un dispositivo potrebbe aver registrato altri dispositivi. Per limitare la creazione di più credenziali per lo stesso account in un singolo autenticatore, puoi ignorare questi dispositivi. Il membro | |
Indica se il dispositivo deve essere collegato alla piattaforma, se non deve esserlo o se non è necessario farlo. Imposta questo valore su | |
| indica il valore |
Crea una credenziale
- Una volta creato un
CreatePublicKeyCredentialRequest(), devi chiamarecreateCredential()con la richiesta creata.
SignUpViewModel.kt
try {
TODO("Call createCredential() with createPublicKeyCredentialRequest")
createCredential(request)
TODO("Complete the registration process after sending public key credential to your server and let the user in")
} catch (e: CreateCredentialException) {
handlePasskeyFailure(e)
}
- Gestisci la visibilità delle visualizzazioni sottoposte a rendering e gestisci le eccezioni se la richiesta non va a buon fine o non riesce per qualche motivo. Qui i messaggi di errore vengono registrati e mostrati nell'app in una finestra di dialogo di errore. Puoi controllare i log degli errori completi tramite Android Studio o il comando
adb debug.

- Infine, devi completare la procedura di registrazione. L'app invia una credenziale di chiave pubblica al server, che la registra per l'utente corrente.
Qui abbiamo utilizzato un server simulato, quindi restituiamo semplicemente il valore true che indica che il server ha salvato la chiave pubblica registrata per scopi di autenticazione e convalida futuri. Per la tua implementazione, puoi scoprire di più sulla registrazione delle passkey lato server.
All'interno del metodo signUpWithPasskeys(), trova il commento pertinente e sostituiscilo con il seguente codice:
SignUpViewModel.kt
try {
createCredential(request)
TODO("Complete the registration process after sending public key credential to your server and let the user in")
registerResponse()
DataProvider.setSignedInThroughPasskeys(true)
_navigationEvent.emit(NavigationEvent.NavigateToHome(signedInWithPasskeys = true))
} catch (e: CreateCredentialException) {
handlePasskeyFailure(e)
}
registerResponse()restituiscetrue, a indicare che il server di simulazione ha salvato la chiave pubblica per un uso futuro.- Imposta il flag
setSignedInThroughPasskeyssutrue. - Una volta eseguito l'accesso, reindirizza l'utente alla schermata Home.
Un vero PublicKeyCredential può contenere più campi. Di seguito è riportato un esempio di questi campi:
{
"id": String,
"rawId": String,
"type": "public-key",
"response": {
"clientDataJSON": String,
"attestationObject": String,
}
}
La tabella seguente spiega alcuni dei parametri importanti in un oggetto PublicKeyCredential:
Parametri | Descrizioni |
Un ID con codifica Base64URL della passkey creata. Questo ID aiuta il browser a determinare se è presente una passkey corrispondente nel dispositivo al momento dell'autenticazione. Questo valore deve essere memorizzato nel database sul backend. | |
Una versione dell'oggetto | |
Un oggetto | |
Un oggetto di attestazione codificato |
Esegui l'app e potrai fare clic sul pulsante Registrati con le passkey e creare una passkey.
4. Salvare una password nel provider di credenziali
In questa app, all'interno della schermata di registrazione, è già implementata una registrazione con nome utente e password a scopo dimostrativo.
Per salvare la credenziale della password utente con il relativo fornitore di password, implementerai un CreatePasswordRequest da passare a createCredential() per salvare la password.
- Trova il metodo
signUpWithPassword(), sostituisci TODO con una chiamatacreatePassword:
SignUpViewModel.kt
TODO("CreatePasswordRequest with entered username and password")
val passwordRequest = CreatePasswordRequest(_username.value, _password.value)
- Poi, crea una credenziale con una richiesta di creazione della password e salva la credenziale della password dell'utente con il relativo fornitore di password. Quindi, accedi come utente. Rileviamo le eccezioni che si verificano in questo flusso in modo più generico. Sostituisci TODO con il seguente codice:
SignUpViewModel.kt
TODO("Create credential with created password request and log the user in")
try {
createCredential(passwordRequest)
simulateServerDelayAndLogIn()
} catch (e: Exception) {
val errorMessage = "Exception Message : " + e.message
Log.e("Auth", errorMessage)
_passwordCreationError.value = errorMessage
_isLoading.value = false
}
Ora hai salvato correttamente la credenziale della password con il fornitore di password dell'utente per l'autenticazione con una password con un solo tocco.
5. Aggiungere la possibilità di autenticarsi con una passkey o una password
Ora puoi utilizzarlo per autenticarti in modo sicuro nella tua app.

Definisci la lambda getCredential() da utilizzare nei modelli di visualizzazione
Come in precedenza, chiameremo getCredential() di Credential Manager in un file separato CredentialManagerUtil.kt per fare riferimento nelle schermate appropriate e passare ai relativi View Model come callback tramite le funzioni lambda.
Individua il commento TODO nella funzione getCredential() in CredentialManagerUtil.kt e chiama la funzione CredentialManager.get():
suspend fun getCredential(
activity: Activity,
request: GetCredentialRequest
): GetCredentialResponse {
TODO("Create a CredentialManager object and call getCredential() with a GetCredentialRequest")
val credentialManager = CredentialManager.create(activity)
return credentialManager.getCredential(activity, request)
}
Ottieni la sfida e altre opzioni da passare alla chiamata getPasskey()
Prima di chiedere all'utente di autenticarsi, devi richiedere i parametri da passare in WebAuthn JSON dal server, inclusa una sfida.
Hai già una risposta simulata nelle risorse (AuthFromServer.txt) che restituisce questi parametri in questo codelab.
- Nella tua app, vai a SignInViewModel.kt, trova il metodo
signInWithSavedCredentialsin cui scriverai la logica per l'autenticazione tramite passkey o password salvata e consenti l'accesso all'utente: - Crea un oggetto GetPublicKeyCredentialOption() con i parametri necessari per ottenere le credenziali dal tuo fornitore di credenziali.
SignInViewModel.kt
TODO("Create a GetPublicKeyCredentialOption() with necessary authentication json from server")
val getPublicKeyCredentialOption =
GetPublicKeyCredentialOption(jsonProvider.fetchAuthJson(), null)
Il metodo fetchAuthJsonFromServer() legge la risposta JSON di autenticazione dagli asset e restituisce il JSON di autenticazione per recuperare tutte le passkey associate a questo account utente.
Il secondo parametro di GetPublicKeyCredentialOption() è clientDataHash, un hash utilizzato per verificare l'identità della relying party. Imposta questo valore solo se hai impostato GetCredentialRequest.origin. Per l'app di esempio, questo valore è impostato su null.
Nota : il server di questo codelab è progettato per restituire un JSON il più simile possibile al dizionario PublicKeyCredentialRequestOptions passato alla chiamata getCredential() dell'API. Il seguente snippet di codice include alcune opzioni di esempio che potresti ricevere in una risposta reale:
{
"challenge": String,
"rpId": String,
"userVerification": "",
"timeout": 1800000
}
La tabella seguente illustra alcuni dei parametri importanti di un oggetto PublicKeyCredentialRequestOptions:
Parametri | Descrizioni |
Una verifica generata dal server in un oggetto | |
Un ID RP è un dominio. Un sito web può specificare il proprio dominio o un suffisso registrabile. Questo valore deve corrispondere al parametro |
- Successivamente, devi creare un oggetto
PasswordOption()per recuperare tutte le password salvate nel tuo fornitore di password tramite l'API Credential Manager per questo account utente. All'interno del metodogetSavedCredentials(), trova TODO e sostituiscilo con quanto segue:
SigninViewModel.kt
TODO("Create a PasswordOption to retrieve all the associated user's password")
val getPasswordOption = GetPasswordOption()
Combinali in un GetCredentialRequest.
SigninViewModel.kt
TODO("Combine requests into a GetCredentialRequest")
val request = GetCredentialRequest(
listOf(
getPublicKeyCredentialOption,
getPasswordOption
)
)
Recupera le credenziali
Successivamente, devi chiamare la richiesta getCredential() con tutte le opzioni precedenti per recuperare le credenziali associate:
SignInViewModel.kt
try {
TODO("Call getCredential() with required credential options")
val result = getCredential(request)
val data = when (result.credential) {
is PublicKeyCredential -> {
val cred = result.credential as PublicKeyCredential
DataProvider.setSignedInThroughPasskeys(true)
"Passkey: ${cred.authenticationResponseJson}"
}
is PasswordCredential -> {
val cred = result.credential as PasswordCredential
DataProvider.setSignedInThroughPasskeys(false)
"Got Password - User:${cred.id} Password: ${cred.password}"
}
is CustomCredential -> {
//If you are also using any external sign-in libraries, parse them here with the utility functions provided.
null
}
else -> null
}
TODO("Complete the authentication process after validating the public key credential to your server and let the user in.")
} catch (e: Exception) {
Log.e("Auth", "getCredential failed with exception: " + e.message.toString())
_signInError.value =
"An error occurred while authenticating: " + e.message.toString()
} finally {
_isLoading.value = false
}
- Trasmetti le informazioni richieste a
getCredential(). Viene visualizzato l'elenco delle opzioni delle credenziali e un contesto di attività per visualizzare le opzioni nel foglio inferiore in quel contesto. - Una volta che la richiesta è andata a buon fine, sullo schermo verrà visualizzato un foglio inferiore con tutte le credenziali create per l'account associato.
- Ora gli utenti possono verificare la propria identità tramite dati biometrici o blocco schermo e così via per autenticare la credenziale scelta.
- Se la credenziale scelta è un
PublicKeyCredential, imposta il flagsetSignedInThroughPasskeyssutrue. In caso contrario, impostalo sufalse.
Il seguente snippet di codice include un esempio di oggetto PublicKeyCredential:
{
"id": String
"rawId": String
"type": "public-key",
"response": {
"clientDataJSON": String
"authenticatorData": String
"signature": String
"userHandle": String
}
}
La seguente tabella non è esaustiva, ma contiene i parametri importanti nell'oggetto PublicKeyCredential:
Parametri | Descrizioni |
L'ID con codifica Base64URL della credenziale passkey autenticata. | |
Una versione dell'oggetto | |
Un oggetto | |
Un oggetto | |
Un oggetto | |
Un oggetto |
- Infine, devi completare la procedura di autenticazione. Normalmente, dopo che l'utente completa l'autenticazione con passkey, l'app invia al server una credenziale di chiave pubblica contenente un'asserzione di autenticazione, che verifica l'asserzione e autentica l'utente.
Qui abbiamo utilizzato un server simulato, quindi restituiamo semplicemente true per indicare che il server ha verificato l'asserzione. Puoi scoprire di più sull'autenticazione passkey lato server per la tua implementazione.
All'interno del metodo signInWithSavedCredentials(), trova il commento pertinente e sostituiscilo con il seguente codice:
SignInViewModel.kt
TODO("Complete the authentication process after validating the public key credential to your server and let the user in.")
if (data != null) {
sendSignInResponseToServer()
_navigationEvent.emit(NavigationEvent.NavigateToHome(signedInWithPasskeys = DataProvider.isSignedInThroughPasskeys()))
}
sendSigninResponseToServer()restituisce true, indicando che il server (simulato) ha convalidato la chiave pubblica per un utilizzo futuro.- Una volta eseguito l'accesso, reindirizza l'utente alla schermata Home.
Esegui l'app e vai ad Accedi > Accedi con le passkey/password salvate e prova ad accedere utilizzando le credenziali salvate.
Prova
Hai implementato la creazione di passkey, il salvataggio della password in Credential Manager e l'autenticazione tramite passkey o password salvata utilizzando l'API Credential Manager nella tua app per Android.
6. Complimenti!
Hai completato questo codelab. Se vuoi controllare la soluzione finale, disponibile all'indirizzo https://github.com/android/identity-samples/tree/main/CredentialManager
In caso di domande, pubblicale su Stack Overflow con il tag passkey.