1. Prima di iniziare
Le soluzioni di autenticazione tradizionali pongono varie sfide in termini di sicurezza e usabilità.
Le password sono ampiamente utilizzate, ma...
- Facilmente dimenticabile
- Gli utenti richiedono conoscenze per creare password efficaci.
- Facile da phishing, raccolta e riproduzione da parte degli aggressori.
Android ha lavorato alla creazione dell'API Gestore delle credenziali 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 le password, Accedi con Google e così via.
Gli utenti potranno creare passkey e memorizzarle nel Gestore delle password di Google, che sincronizza le passkey sui dispositivi Android su cui l'utente ha eseguito l'accesso. Per consentire agli utenti di accedere alla passkey, è necessario creare una passkey, associarla a un account utente e archiviare la sua chiave pubblica su un server.
In questo codelab, imparerai a registrarti utilizzando passkey e password tramite l'API Credential Manager e a utilizzarle per l'autenticazione futuri. Esistono due flussi:
- Registrati : utilizzando passkey e password.
- Accedere : tramite passkey e password salvata.
Prerequisiti
- Conoscenza di base dell'esecuzione di 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 in 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 della password tramite l'API Credential Manager).
- Dispositivo preferibilmente dotato di sensore biometrico.
- Assicurati di registrare un dato biometrico (o un blocco schermo).
- Versione plug-in Kotlin : 1.8.10
2. Configurazione
- Clona questo repository sul tuo laptop dalla filiale credman_codelab : https://github.com/android/identity-samples/tree/credman_codelab
- Vai al modulo CredentialManager e apri il progetto in Android Studio.
Vediamo lo stato iniziale dell'app
Per controllare il funzionamento dello stato iniziale dell'app, segui questi passaggi:
- Avvia l'app.
- Viene visualizzata una schermata principale con un pulsante di registrazione e accesso.
- Puoi fare clic su Registrati per registrarti utilizzando una passkey o una password.
- Puoi fare clic su Accedi per usare passkey e password salvata.
Per capire cosa sono le passkey e come funzionano, vedi Come funzionano le passkey? .
3. Aggiungi la possibilità di registrarsi usando le passkey
Quando si registrano per un nuovo account su un'app per Android che utilizza l'API Credential Manager, gli utenti possono creare una passkey per il proprio account. Questa passkey verrà memorizzata in modo sicuro sul fornitore di credenziali scelto dall'utente e utilizzata per gli accessi futuri, senza che l'utente debba inserire la password ogni volta.
Ora creerai una passkey e registrerai le credenziali utente usando la biometria/il blocco schermo.
Registrati con la passkey
Inside Credential Manager -> app -> principale -> java -> SignUpFragment.kt, vedrai il campo di testo "nome utente" e un pulsante per registrarti con la passkey.
Supera la sfida e altre risposte json per la chiamata createPasskey()
Prima di creare una passkey, devi richiedere al server di ottenere le informazioni necessarie da passare all'API Credential Manager durante la chiamata createCredential().
Fortunatamente, hai già una risposta fittizia nei tuoi asset(RegFromServer.txt) che restituisce questi parametri in questo codelab.
- Nell'app, vai al metodo SignUpFragment.kt, Trova, signUpWithPasskeys, dove scriverai la logica per creare una passkey e consentire all'utente di accedere. Puoi trovare il metodo nella stessa classe.
- Seleziona il blocco "other" con un commento per chiamare la funzione createPasskey() e sostituiscila con il seguente codice :
SignUpFragment.kt
//TODO : Call createPasskey() to signup with passkey
val data = createPasskey()
Questo metodo verrà chiamato una volta che avrai compilato un nome utente valido sullo schermo.
- All'interno del metodo createPasskey(), devi creare un'istruzione CreatePublicKeyCredentialRequest() con i parametri necessari restituiti.
SignUpFragment.kt
//TODO create a CreatePublicKeyCredentialRequest() with necessary registration json from server
val request = CreatePublicKeyCredentialRequest(fetchRegistrationJsonFromServer())
Questo metodo fetchRegistrationJsonFromServer() legge la risposta json della registrazione dagli asset e restituisce il json della registrazione da passare durante la creazione della passkey.
- Trova il metodo fetchRegistrationJsonFromServer() e sostituisci TODO con il seguente codice per restituire il json e rimuovi l'istruzione di restituzione della stringa vuota :
SignUpFragment.kt
//TODO fetch registration mock response
val response = requireContext().readFromAsset("RegFromServer")
//Update userId,challenge, name and Display name in the mock
return response.replace("<userId>", getEncodedUserId())
.replace("<userName>", binding.username.text.toString())
.replace("<userDisplayName>", binding.username.text.toString())
.replace("<challenge>", getEncodedChallenge())
- Qui leggi il json della registrazione dalle risorse.
- Questo json ha 4 campi da sostituire.
- L'ID utente deve essere univoco per consentire a un utente di creare più passkey (se necessario). Tu sostituisci <userId> con lo userId generato.
- <challenge> deve essere univoco, quindi verrà generato un test univoco casuale. Il metodo è già presente nel codice.
Il seguente snippet di codice include opzioni di esempio ricevute dal server:
{
"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 non è esaustiva, ma contiene i parametri importanti nel dizionario PublicKeyCredentialCreationOptions
:
Parametri | Descrizioni |
Una stringa casuale generata dal server che contiene abbastanza entropia da rendere inattuabile l'ipotesi. Deve avere una lunghezza di almeno 16 byte. Questi dati sono obbligatori, ma non vengono utilizzati durante la registrazione, salvo 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 andrà bene. | |
Questo campo deve contenere un identificatore univoco dell'account che l'utente riconoscerà, come l'indirizzo email o il nome utente. Questo valore verrà visualizzato nel selettore degli account. Se usi un nome utente, usa lo stesso valore dell'autenticazione tramite password. | |
Questo campo è un nome facoltativo e più intuitivo per l'account. È un nome accessibile alle persone per l'account utente, destinato solo alla visualizzazione. | |
L'entità parte integrante corrisponde ai dettagli della tua richiesta.Ha bisogno di :
| |
Parametri credenziali chiave pubblica è un elenco di algoritmi e 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 su un singolo autenticatore, puoi ignorare questi dispositivi. Il membro trasporti, se fornito, deve contenere il risultato della chiamata a getTransports() durante la registrazione di ogni credenziale. | |
indica se il dispositivo deve essere collegato alla piattaforma o meno o se non sussiste alcun requisito. Impostala su "platform". Questo indica che vogliamo un autenticatore incorporato nel dispositivo della piattaforma e all'utente non verrà chiesto di inserire, ad esempio, Un token di sicurezza USB. | |
| indica il valore "required" per creare una passkey. |
Crea una credenziale
- Dopo aver creato un oggetto CreatePublicKeyCredentialRequest(), devi chiamare la chiamata createCredential() con la richiesta creata.
SignUpFragment.kt
//TODO call createCredential() with createPublicKeyCredentialRequest
try {
response = credentialManager.createCredential(
requireActivity(),
request
) as CreatePublicKeyCredentialResponse
} catch (e: CreateCredentialException) {
configureProgress(View.INVISIBLE)
handlePasskeyFailure(e)
}
- Devi passare le informazioni richieste a createCredential().
- Una volta approvata la richiesta, sullo schermo verrà visualizzato un riquadro inferiore in cui ti viene chiesto di creare una passkey.
- Ora gli utenti possono verificare la propria identità tramite la biometria, il blocco schermo e così via.
- Puoi gestire la visibilità delle visualizzazioni visualizzate e le eccezioni se la richiesta non riesce o non va a buon fine 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 completi degli errori tramite il comando di debug di Android Studio o adb.
- Infine, devi completare il processo di registrazione inviando la credenziale della chiave pubblica al server e facendo accedere l'utente. L'app riceve un oggetto credenziali contenente una chiave pubblica che puoi inviare al server per registrare la passkey.
In questo caso abbiamo utilizzato un server fittizio, quindi restituiamo semplicemente "true" che indica che il server ha salvato la chiave pubblica registrata per scopi di autenticazione e convalida futuri.
All'interno del metodo signUpWithPasskeys(), trova il commento pertinente e sostituiscilo con il seguente codice:
SignUpFragment.kt
//TODO : complete the registration process after sending public key credential to your server and let the user in
data?.let {
registerResponse()
DataProvider.setSignedInThroughPasskeys(true)
listener.showHome()
}
- registryResponse restituisce true che indica che il server (simulativo) ha salvato la chiave pubblica per un uso futuro.
- Hai impostato il flagSignedInTramitePasskeys su true per indicare che stai accedendo tramite passkey.
- Una volta effettuato l'accesso, l'utente verrà reindirizzato alla schermata Home.
Il seguente snippet di codice contiene un esempio di opzioni che dovresti ricevere:
{
"id": String,
"rawId": String,
"type": "public-key",
"response": {
"clientDataJSON": String,
"attestationObject": String,
}
}
La tabella seguente non è esaustiva, ma contiene i parametri importanti in PublicKeyCredential
:
Parametri | Descrizioni |
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 del backend. | |
Una versione dell'oggetto | |
Un oggetto | |
Un oggetto di attestazione codificato |
Esegui l'app: potrai fare clic sul pulsante Registrati con le passkey e creare una passkey.
4. Salva la password in Provider di credenziali
In questa app, nella schermata di registrazione, è già presente una registrazione con nome utente e password implementati a scopo dimostrativo.
Per salvare la credenziale della password dell'utente con il relativo fornitore di password, devi implementare una richiesta CreatePasswordRequest che devi passare a createCredential() per salvare la password.
- Trova il metodo signUpWithPassword(), sostituisci TODO con createPassword :
SignUpFragment.kt
//TODO : Save the user credential password with their password provider
createPassword()
- All'interno del metodo createPassword(), devi creare una richiesta di password come questa, sostituire TODO con il seguente codice :
SignUpFragment.kt
//TODO : CreatePasswordRequest with entered username and password
val request = CreatePasswordRequest(
binding.username.text.toString(),
binding.password.text.toString()
)
- Quindi, all'interno del metodo createPassword(), devi creare la credenziale con la richiesta di creazione della password e salvare la credenziale della password dell'utente con il relativo provider della password, sostituire il TODO con il seguente codice :
SignUpFragment.kt
//TODO : Create credential with created password request
try {
credentialManager.createCredential(request, requireActivity()) as CreatePasswordResponse
} catch (e: Exception) {
Log.e("Auth", " Exception Message : " + e.message)
}
- Ora hai salvato correttamente la credenziale della password con il provider della password dell'utente per autenticarla tramite password con un solo tocco.
5. Aggiungi la possibilità di eseguire l'autenticazione con una passkey o una password
Ora puoi utilizzarlo come metodo per eseguire l'autenticazione sicura per la tua app.
Ottieni la sfida e altre opzioni da superare per la chiamata getPasskey()
Prima di chiedere all'utente di eseguire l'autenticazione, devi richiedere i parametri da passare nel json WebAuthn dal server, inclusa una verifica.
Hai già una risposta fittizia nei tuoi asset(AuthFromServer.txt) che restituisce questi parametri in questo codelab.
- Nell'app, vai al metodo SignInFragment.kt, trova il metodo
signInWithSavedCredentials
in cui scriverai la logica per l'autenticazione tramite passkey o password salvata e consenti all'utente di accedere : - Seleziona il blocco "other" con un commento per chiamare la funzione createPasskey() e sostituiscila con il seguente codice :
SignInFragment.kt
//TODO : Call getSavedCredentials() method to signin using passkey/password
val data = getSavedCredentials()
- All'interno del metodo getSavedCredentials(), devi creare una
GetPublicKeyCredentialOption
() con i parametri necessari per ottenere le credenziali dal tuo provider di credenziali.
SigninFragment.kt
//TODO create a GetPublicKeyCredentialOption() with necessary registration json from server
val getPublicKeyCredentialOption =
GetPublicKeyCredentialOption(fetchAuthJsonFromServer(), null)
Questo metodo fetchAuthJsonFromServer() legge la risposta del json di autenticazione dagli asset e restituisce il json di autenticazione per recuperare tutte le passkey associate a questo account utente.
Secondo parametro : clientDataHash. Un hash utilizzato per verificare l'identità del componente, impostato solo se hai impostato GetCredentialRequest.origin. Per l'app di esempio, questo valore è null.
Il terzo parametro è true se preferisci che l'operazione restituisca immediatamente quando non sono disponibili credenziali, invece di ricorrere al rilevamento delle credenziali remote; in caso contrario, è false (impostazione predefinita).
- Trova il metodo fetchAuthJsonFromServer() e sostituisci TODO con il seguente codice per restituire il json e rimuovi l'istruzione di ritorno con stringa vuota :
SignInFragment.kt
//TODO fetch authentication mock json
return requireContext().readFromAsset("AuthFromServer")
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 alcuni esempi di opzioni che dovresti ricevere:
{
"challenge": String,
"rpId": String,
"userVerification": "",
"timeout": 1800000
}
La tabella seguente non è esaustiva, ma contiene i parametri importanti nel dizionario PublicKeyCredentialRequestOptions
:
Parametri | Descrizioni |
Una verifica generata dal server in un oggetto | |
Un ID parte soggetta a limitazioni è un dominio. Un sito web può specificare il proprio dominio o un suffisso registrabile. Questo valore deve corrispondere al parametro |
- Successivamente, dovrai creare un oggetto PasswordOption() per recuperare tutte le password salvate salvate nel provider di password tramite l'API Credential Manager per questo account utente. All'interno del metodo getSavedCredentials(), trova il TODO e sostituiscilo con quanto segue :
SigninFragment.kt
//TODO create a PasswordOption to retrieve all the associated user's password
val getPasswordOption = GetPasswordOption()
Recupera le credenziali
- Successivamente, devi chiamare la richiesta getCredential() con tutte le opzioni precedenti per recuperare le credenziali associate :
SignInFragment.kt
//TODO call getCredential() with required credential options
val result = try {
credentialManager.getCredential(
requireActivity(),
GetCredentialRequest(
listOf(
getPublicKeyCredentialOption,
getPasswordOption
)
)
)
} catch (e: Exception) {
configureViews(View.INVISIBLE, true)
Log.e("Auth", "getCredential failed with exception: " + e.message.toString())
activity?.showErrorAlert(
"An error occurred while authenticating through saved credentials. Check logs for additional details"
)
return null
}
if (result.credential is PublicKeyCredential) {
val cred = result.credential as PublicKeyCredential
DataProvider.setSignedInThroughPasskeys(true)
return "Passkey: ${cred.authenticationResponseJson}"
}
if (result.credential is PasswordCredential) {
val cred = result.credential as PasswordCredential
DataProvider.setSignedInThroughPasskeys(false)
return "Got Password - User:${cred.id} Password: ${cred.password}"
}
if (result.credential is CustomCredential) {
//If you are also using any external sign-in libraries, parse them here with the utility functions provided.
}
- Devi passare le informazioni richieste a getCredential(). Viene utilizzato l'elenco delle opzioni per le credenziali e un contesto dell'attività per visualizzare le opzioni nel riquadro inferiore di quel contesto.
- Una volta approvata la richiesta, sullo schermo verrà visualizzato un riquadro inferiore con l'elenco di tutte le credenziali create per l'account associato.
- Ora gli utenti possono verificare la propria identità tramite la biometria, il blocco schermo e così via per autenticare la credenziale scelta.
- Hai impostato il flagSignedInTramitePasskeys su true per indicare che stai accedendo tramite passkey. In caso contrario, è false.
- Puoi gestire la visibilità delle visualizzazioni visualizzate e le eccezioni se la richiesta non riesce o non va a buon fine 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 completi degli errori tramite il comando di debug di Android Studio o adb.
- Infine, devi completare il processo di registrazione inviando la credenziale della chiave pubblica al server e facendo accedere l'utente. L'app riceve un oggetto credenziali contenente una chiave pubblica che puoi inviare al server per l'autenticazione tramite passkey.
In questo caso abbiamo utilizzato un server fittizio, quindi restituiamo semplicemente true che indica che il server ha convalidato la chiave pubblica.
All'interno del metodo signInWithSavedCredentials
(), trova il commento pertinente e sostituiscilo con il seguente codice:
SignInFragment.kt
//TODO : complete the authentication process after validating the public key credential to your server and let the user in.
data?.let {
sendSignInResponseToServer()
listener.showHome()
}
- sendSigninResponseToServer() restituisce true che indica che il server (simulativo) ha convalidato la chiave pubblica per l'uso futuro.
- Una volta effettuato l'accesso, l'utente verrà reindirizzato alla schermata Home.
Il seguente snippet di codice include un oggetto PublicKeyCredential
di esempio:
{
"id": String
"rawId": String
"type": "public-key",
"response": {
"clientDataJSON": String
"authenticatorData": String
"signature": String
"userHandle": String
}
}
La tabella seguente non è esaustiva, ma contiene i parametri importanti nell'oggetto PublicKeyCredential
:
Parametri | Descrizioni |
L'ID codificato Base64URL della credenziale passkey autenticata. | |
Una versione dell'oggetto | |
Un oggetto | |
Un oggetto | |
Un oggetto | |
Un oggetto |
Esegui l'app, vai alla pagina di accesso -> Accedi con passkey/password salvate e prova ad accedere usando le credenziali salvate.
Prova
Hai implementato la creazione di passkey, il salvataggio delle password in Gestore delle credenziali e l'autenticazione tramite passkey o password salvate usando l'API Credential Manager nella tua app per Android.
6. Complimenti!
Hai completato il codelab. Se vuoi controllare la risoluzione finale, puoi trovarla all'indirizzo https://github.com/android/identity-samples/tree/main/CredentialManager.
Poni eventuali domande su StackOverflow con un tag passkey
.