Consenti l'accesso all'utente con Gestore credenziali

Credential Manager è un'API Jetpack che supporta più metodi di accesso, come nome utente e password, passkey e soluzioni di accesso federato (come Accedi con Google), in un'unica API, semplificando l'integrazione per gli sviluppatori.

Inoltre, per gli utenti, Gestore delle credenziali unifica l'interfaccia di accesso tra i diversi metodi di autenticazione, rendendo più chiaro e facile l'accesso alle app per gli utenti, indipendentemente dal metodo scelto.

Questa pagina spiega il concetto di passkey e i passaggi per implementare il supporto lato client per le soluzioni di autenticazione, incluse le passkey, utilizzando l'API Credential Manager. C'è anche una pagina delle domande frequenti separata che fornisce risposte a domande più dettagliate e specifiche.

Il tuo feedback è fondamentale per migliorare l'API Credential Manager. Condividi eventuali problemi riscontrati o idee per migliorare l'API utilizzando il seguente link:

Lascia un feedback

Informazioni sulle passkey

Le passkey sostituiscono le password in modo più sicuro e semplice Con le passkey, gli utenti possono accedere ad app e siti web utilizzando un sensore biometrico (ad esempio le impronte o il riconoscimento facciale), un PIN o una sequenza. In questo modo viene offerta un'esperienza di accesso ottimale, in modo che gli utenti non debbano ricordare nomi utente o password.

Le passkey si basano su WebAuthn (Web Authentication), uno standard sviluppato congiuntamente dalla FIDO Alliance e dal World Wide Web Consortium (W3C). WebAuthn utilizza la crittografia a chiave pubblica per autenticare l'utente. Il sito web o l'app a cui l'utente accede può visualizzare e archiviare la chiave pubblica, ma mai la chiave privata. La chiave privata viene mantenuta segreta e protetta. Inoltre, poiché la chiave è univoca e legata al sito web o all'app, le passkey non consentono il phishing, aumentandone la sicurezza.

Gestore delle credenziali consente agli utenti di creare passkey e di archiviarle in Gestore delle password di Google.

Prerequisiti

Per utilizzare Gestore delle credenziali, completa i passaggi descritti in questa sezione.

Utilizza una versione recente della piattaforma

Gestore delle credenziali è supportato su Android 4.4 (livello API 19) e versioni successive.

Aggiungi dipendenze alla tua app

Aggiungi le seguenti dipendenze allo script di build del modulo dell'app:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.3.0-alpha03")

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation("androidx.credentials:credentials-play-services-auth:1.3.0-alpha03")
}

Groovy

dependencies {
    implementation "androidx.credentials:credentials:1.3.0-alpha03"

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation "androidx.credentials:credentials-play-services-auth:1.3.0-alpha03"
}

Conserva le classi nel file ProGuard

Nel file proguard-rules.pro del modulo, aggiungi le seguenti istruzioni:

-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
  *;
}

Scopri di più su come ridurre, offuscare e ottimizzare la tua app.

Aggiungere il supporto per Digital Asset Links

Per attivare il supporto delle passkey per la tua app per Android, associa l'app a un sito web di proprietà dell'app. Per dichiarare questa associazione, procedi nel seguente modo:

  1. Crea un file JSON Digital Asset Links. Ad esempio, per dichiarare che il sito web https://signin.example.com e un'app Android con il nome del pacchetto com.example possono condividere le credenziali di accesso, crea un file denominato assetlinks.json con i seguenti contenuti:

    [
      {
        "relation" : [
          "delegate_permission/common.handle_all_urls",
          "delegate_permission/common.get_login_creds"
        ],
        "target" : {
          "namespace" : "android_app",
          "package_name" : "com.example.android",
          "sha256_cert_fingerprints" : [
            SHA_HEX_VALUE
          ]
        }
      }
    ]
    

    Il campo relation è un array di una o più stringhe che descrivono la relazione dichiarata. Per dichiarare che app e siti condividono le credenziali di accesso, specifica le relazioni come delegate_permission/handle_all_urls e delegate_permission/common.get_login_creds.

    Il campo target è un oggetto che specifica l'asset a cui si applica la dichiarazione. I seguenti campi identificano un sito web:

    namespace web
    site

    L'URL del sito web, nel formato https://domain[:optional_port]; ad esempio, https://www.example.com.

    domain deve essere completo e optional_port deve essere omesso quando si utilizza la porta 443 per HTTPS.

    Una destinazione site può essere solo un dominio principale: non puoi limitare l'associazione di un'app a una sottodirectory specifica. Non includere un percorso nell'URL, ad esempio una barra finale.

    I sottodomini non vengono considerati corrispondenti, ovvero se specifichi domain come www.example.com, il dominio www.counter.example.com non è associato alla tua app.

    I seguenti campi identificano un'app per Android:

    namespace android_app
    package_name Il nome del pacchetto dichiarato nel file manifest dell'app. Ad esempio: com.example.android
    sha256_cert_fingerprints Le fingerprint SHA256 del certificato di firma dell'app.
  2. Ospita il file JSON Digital Assets Link nella seguente posizione nel dominio di accesso:

    https://domain[:optional_port]/.well-known/assetlinks.json
    

    Ad esempio, se il dominio di accesso è signin.example.com, ospita il file JSON all'indirizzo https://signin.example.com/.well-known/assetlinks.json.

    Il tipo MIME per il file Digital Asset Link deve essere JSON. Assicurati che il server invii un'intestazione Content-Type: application/json nella risposta.

  3. Assicurati che il tuo host permetta a Google di recuperare il tuo file Digital Asset Link. Se hai un file robots.txt, questo deve consentire all'agente Googlebot di recuperare /.well-known/assetlinks.json. La maggior parte dei siti può consentire a qualsiasi agente automatico di recuperare i file nel percorso /.well-known/ in modo che altri servizi possano accedere ai metadati in questi file:

    User-agent: *
    Allow: /.well-known/
    
  4. Aggiungi la seguente riga al file manifest in <application>:

    <meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
    
  5. Se utilizzi l'accesso tramite password tramite Gestore delle credenziali, segui questo passaggio per configurare il collegamento degli asset digitali nel file manifest. Questo passaggio non è necessario se utilizzi solo le passkey.

    Dichiara l'associazione nell'app per Android. Aggiungi un oggetto che specifichi i file assetlinks.json da caricare. Devi eseguire l'escape di tutti gli apostrofi e delle virgolette che utilizzi nella stringa. Ecco alcuni esempi:

    <string name="asset_statements" translatable="false">
    [{
      \"include\": \"https://signin.example.com/.well-known/assetlinks.json\"
    }]
    </string>
    
    > GET /.well-known/assetlinks.json HTTP/1.1
    > User-Agent: curl/7.35.0
    > Host: signin.example.com
    
    < HTTP/1.1 200 OK
    < Content-Type: application/json
    

Configurare Gestore delle credenziali

Per configurare e inizializzare un oggetto CredentialManager, aggiungi una logica simile alla seguente:

Kotlin

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
val credentialManager = CredentialManager.create(context)

Java

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
CredentialManager credentialManager = CredentialManager.create(context)

Indica i campi delle credenziali

Su Android 14 e versioni successive, l'attributo isCredential può essere utilizzato per indicare i campi delle credenziali, ad esempio i campi del nome utente o della password. Questo attributo indica che questa vista è un campo delle credenziali che funziona con Gestore delle credenziali e fornitori di credenziali di terze parti, aiutando al contempo i servizi di compilazione automatica a fornire suggerimenti di compilazione automatica migliori. Quando l'app utilizza l'API Credential Manager, viene visualizzato il riquadro inferiore di Credential Manager con le credenziali disponibili e non è più necessario mostrare la finestra di dialogo di compilazione automatica per nome utente o password.

Per utilizzare l'attributo isCredential, aggiungilo alle viste pertinenti:

<TextView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:isCredential="true"
...
 />

Fai accedere l'utente

Per recuperare tutte le opzioni per passkey e password associate all'account dell'utente, completa questi passaggi:

  1. Inizializza le opzioni di autenticazione tramite password e passkey:

    Kotlin

    // Retrieves the user's saved password for your app from their
    // password provider.
    val getPasswordOption = GetPasswordOption()
    
    // Get passkey from the user's public key credential provider.
    val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
        requestJson = requestJson
    )

    Java

    // Retrieves the user's saved password for your app from their
    // password provider.
    GetPasswordOption getPasswordOption = new GetPasswordOption();
    
    // Get passkey from the user's public key credential provider.
    GetPublicKeyCredentialOption getPublicKeyCredentialOption =
            new GetPublicKeyCredentialOption(requestJson);
  2. Utilizza le opzioni recuperate dal passaggio precedente per creare la richiesta di accesso.

    Kotlin

    val getCredRequest = GetCredentialRequest(
        listOf(getPasswordOption, getPublicKeyCredentialOption)
    )

    Java

    GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder()
        .addCredentialOption(getPasswordOption)
        .addCredentialOption(getPublicKeyCredentialOption)
        .build();
  3. Avviare il flusso di accesso:

    Kotlin

    coroutineScope.launch {
        try {
            val result = credentialManager.getCredential(
                // Use an activity-based context to avoid undefined system UI
                // launching behavior.
                context = activityContext,
                request = getCredRequest
            )
            handleSignIn(result)
        } catch (e : GetCredentialException) {
            handleFailure(e)
        }
    }
    
    fun handleSignIn(result: GetCredentialResponse) {
        // Handle the successfully returned credential.
        val credential = result.credential
    
        when (credential) {
            is PublicKeyCredential -> {
                val responseJson = credential.authenticationResponseJson
                // Share responseJson i.e. a GetCredentialResponse on your server to
                // validate and  authenticate
            }
            is PasswordCredential -> {
                val username = credential.id
                val password = credential.password
                // Use id and password to send to your server to validate
                // and authenticate
            }
          is CustomCredential -> {
              // If you are also using any external sign-in libraries, parse them
              // here with the utility functions provided.
              if (credential.type == ExampleCustomCredential.TYPE)  {
              try {
                  val ExampleCustomCredential = ExampleCustomCredential.createFrom(credential.data)
                  // Extract the required credentials and complete the authentication as per
                  // the federated sign in or any external sign in library flow
                  } catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
                      // Unlikely to happen. If it does, you likely need to update the dependency
                      // version of your external sign-in library.
                      Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
                  }
              } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential")
              }
            } else -> {
                // Catch any unrecognized credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }
    }

    Java

    credentialManager.getCredentialAsync(
        // Use activity based context to avoid undefined
        // system UI launching behavior
        activity,
        getCredRequest,
        cancellationSignal,
        <executor>,
        new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
            @Override
            public void onResult(GetCredentialResponse result) {
                handleSignIn(result);
            }
    
            @Override
            public void onError(GetCredentialException e) {
                handleFailure(e);
            }
        }
    );
    
    public void handleSignIn(GetCredentialResponse result) {
        // Handle the successfully returned credential.
        Credential credential = result.getCredential();
        if (credential instanceof PublicKeyCredential) {
            String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
            // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
        } else if (credential instanceof PasswordCredential) {
            String username = ((PasswordCredential) credential).getId();
            String password = ((PasswordCredential) credential).getPassword();
            // Use id and password to send to your server to validate and authenticate
        } else if (credential instanceof CustomCredential) {
            if (ExampleCustomCredential.TYPE.equals(credential.getType())) {
                try {
                    ExampleCustomCredential customCred = ExampleCustomCredential.createFrom(customCredential.getData());
                    // Extract the required credentials and complete the
                    // authentication as per the federated sign in or any external
                    // sign in library flow
                } catch (ExampleCustomCredential.ExampleCustomCredentialParsingException e) {
                    // Unlikely to happen. If it does, you likely need to update the
                    // dependency version of your external sign-in library.
                    Log.e(TAG, "Failed to parse an ExampleCustomCredential", e);
                }
            } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential");
            }
        } else {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential");
        }
    }

L'esempio seguente mostra come formattare la richiesta JSON quando ricevi una passkey:

{
  "challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
  "allowCredentials": [],
  "timeout": 1800000,
  "userVerification": "required",
  "rpId": "credential-manager-app-test.glitch.me"
}

L'esempio seguente mostra l'aspetto di una risposta JSON dopo aver ottenuto una credenziale di chiave pubblica:

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
    "signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
    "userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
  }
}

Gestisci le eccezioni quando non sono disponibili credenziali

In alcuni casi, l'utente potrebbe non disporre di credenziali o potrebbe non concedere il consenso all'utilizzo di una credenziale disponibile. Se viene richiamato getCredential() e non vengono trovate credenziali, viene restituito un NoCredentialException. In questo caso, il tuo codice dovrebbe gestire le istanze NoCredentialException.

Kotlin

try {
  val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
  Log.e("CredentialManager", "No credential available", e)
}

Java

try {
  Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
  Log.e("CredentialManager", "No credential available", e);
}

Su Android 14 o versioni successive, puoi ridurre la latenza quando mostri il selettore account utilizzando il metodo prepareGetCredential() prima della chiamata getCredential().

Kotlin

val response = credentialManager.prepareGetCredential(
  GetCredentialRequest(
    listOf(
      <getPublicKeyCredentialOption>,
      <getPasswordOption>
    )
  )
}

Java

GetCredentialResponse response = credentialManager.prepareGetCredential(
  new GetCredentialRequest(
    Arrays.asList(
      new PublicKeyCredentialOption(),
      new PasswordOption()
    )
  )
);

Il metodo prepareGetCredential() non richiama gli elementi dell'interfaccia utente. Ti aiuta solo a eseguire la preparazione, in modo da poter successivamente lanciare le restanti operazioni get-credential (che coinvolgono le UI) tramite l'API getCredential().

I dati memorizzati nella cache vengono restituiti in un oggetto PrepareGetCredentialResponse. Se esistono già delle credenziali, i risultati verranno memorizzati nella cache e potrai successivamente avviare l'API getCredential() rimanente per visualizzare il selettore di account con i dati memorizzati nella cache.

Flussi di registrazione

Puoi registrare un utente per l'autenticazione utilizzando una passkey o una password.

Creare una passkey

Per offrire agli utenti la possibilità di registrare una passkey e di utilizzarla per la riautenticazione, registra una credenziale utente utilizzando un oggetto CreatePublicKeyCredentialRequest.

Kotlin

fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) {
    val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
        // Contains the request in JSON format. Uses the standard WebAuthn
        // web JSON spec.
        requestJson = requestJson,
        // Defines whether you prefer to use only immediately available
        // credentials, not hybrid credentials, to fulfill this request.
        // This value is false by default.
        preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
    )

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    coroutineScope.launch {
        try {
            val result = credentialManager.createCredential(
                // Use an activity-based context to avoid undefined system
                // UI launching behavior
                context = activityContext,
                request = createPublicKeyCredentialRequest,
            )
            handlePasskeyRegistrationResult(result)
        } catch (e : CreateCredentialException){
            handleFailure(e)
        }
    }
}

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
            handlePasskeyError(e.domError)
        }
        is CreateCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to register the credential.
        }
        is CreateCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is CreateCredentialProviderConfigurationException -> {
            // Your app is missing the provider configuration dependency.
            // Most likely, you're missing the
            // "credentials-play-services-auth" module.
        }
        is CreateCredentialUnknownException -> ...
        is CreateCredentialCustomException -> {
            // You have encountered an error from a 3rd-party SDK. If you
            // make the API call with a request object that's a subclass of
            // CreateCustomCredentialRequest using a 3rd-party SDK, then you
            // should check for any custom exception type constants within
            // that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
    }
}

Java

public void createPasskey(String requestJson, boolean preferImmediatelyAvailableCredentials) {
    CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
            // `requestJson` contains the request in JSON format. Uses the standard
            // WebAuthn web JSON spec.
            // `preferImmediatelyAvailableCredentials` defines whether you prefer
            // to only use immediately available credentials, not  hybrid credentials,
            // to fulfill this request. This value is false by default.
            new CreatePublicKeyCredentialRequest(
                requestJson, preferImmediatelyAvailableCredentials);

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined system
        // UI launching behavior
        requireActivity(),
        createPublicKeyCredentialRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleSuccessfulCreatePasskeyResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                if (e instanceof CreatePublicKeyCredentialDomException) {
                    // Handle the passkey DOM errors thrown according to the
                    // WebAuthn spec.
                    handlePasskeyError(((CreatePublicKeyCredentialDomException)e).getDomError());
                } else if (e instanceof CreateCredentialCancellationException) {
                    // The user intentionally canceled the operation and chose not
                    // to register the credential.
                } else if (e instanceof CreateCredentialInterruptedException) {
                    // Retry-able error. Consider retrying the call.
                } else if (e instanceof CreateCredentialProviderConfigurationException) {
                    // Your app is missing the provider configuration dependency.
                    // Most likely, you're missing the
                    // "credentials-play-services-auth" module.
                } else if (e instanceof CreateCredentialUnknownException) {
                } else if (e instanceof CreateCredentialCustomException) {
                    // You have encountered an error from a 3rd-party SDK. If
                    // you make the API call with a request object that's a
                    // subclass of
                    // CreateCustomCredentialRequest using a 3rd-party SDK,
                    // then you should check for any custom exception type
                    // constants within that SDK to match with e.type.
                    // Otherwise, drop or log the exception.
                } else {
                  Log.w(TAG, "Unexpected exception type "
                          + e.getClass().getName());
                }
            }
        }
    );
}

Formatta la richiesta JSON

Dopo aver creato una passkey, devi associarla all'account di un utente e archiviare la chiave pubblica della passkey sul tuo server. L'esempio di codice seguente mostra come formattare la richiesta JSON quando crei una passkey.

Questo post del blog su come integrare un'autenticazione perfetta nelle tue app mostra come formattare la richiesta JSON quando crei passkey e quando esegui l'autenticazione utilizzando le passkey. Spiega inoltre perché le password non sono una soluzione di autenticazione efficace, come sfruttare le credenziali biometriche esistenti, come associare la tua app a un sito web di tua proprietà, come creare passkey e come autenticarti con le passkey.

{
  "challenge": "abc123",
  "rp": {
    "name": "Credential Manager example",
    "id": "credential-manager-test.example.com"
  },
  "user": {
    "id": "def456",
    "name": "helloandroid@gmail.com",
    "displayName": "helloandroid@gmail.com"
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [
    {"id": "ghi789", "type": "public-key"},
    {"id": "jkl012", "type": "public-key"}
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

Imposta i valori per authenticatorAttachment

Il parametro authenticatorAttachment può essere impostato solo al momento della creazione delle credenziali. Puoi specificare platform, cross-platform o nessun valore. Nella maggior parte dei casi, non è consigliato alcun valore.

  • platform: per registrare il dispositivo attuale dell'utente o per chiedere a un utente con password di eseguire l'upgrade alle passkey dopo l'accesso, imposta authenticatorAttachment su platform.
  • cross-platform: questo valore viene comunemente utilizzato durante la registrazione di credenziali a più fattori e non viene utilizzato in un contesto di passkey.
  • Nessun valore: per offrire agli utenti la flessibilità di creare passkey sui loro dispositivi preferiti (ad esempio nelle impostazioni dell'account), il parametro authenticatorAttachment non deve essere specificato quando un utente sceglie di aggiungere una passkey. Nella maggior parte dei casi, non specificare il parametro è l'opzione migliore.

Evitare la creazione di passkey duplicate

Elenca gli ID credenziali nell'array facoltativo excludeCredentials per impedire la creazione di una nuova passkey se ne esiste già una con lo stesso fornitore.

Gestire la risposta JSON

Il seguente snippet di codice mostra un esempio di risposta JSON per la creazione di una credenziale di chiave pubblica. Scopri di più su come gestire le credenziali della chiave pubblica restituita.

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
  }
}

Verifica l'origine da dati JSON client

origin rappresenta l'applicazione o il sito web da cui proviene una richiesta e viene utilizzato dalle passkey per proteggere dagli attacchi di phishing. Il server dell'app è necessario per controllare l'origine dei dati del client in base a una lista consentita di app e siti web approvati. Se il server riceve una richiesta da un'app o da un sito web da un'origine non riconosciuta, la richiesta dovrebbe essere rifiutata.

Nel caso del web, origin riflette l'origine dello stesso sito in cui è stata effettuata l'accesso alla credenziale. Ad esempio, dato l'URL di https://www.example.com:8443/store?category=shoes#athletic , origin è https://www.example.com:8443.

Per le app per Android, lo user agent imposta automaticamente origin sulla firma dell'app per le chiamate. Questa firma deve essere verificata come corrispondenza sul tuo server per convalidare il chiamante dell'API passkey. L'origin di Android è un URI derivato dall'hash SHA-256 del certificato di firma dell'APK, ad esempio:

android:apk-key-hash:<sha256_hash-of-apk-signing-cert>

Gli hash SHA-256 dei certificati di firma da un archivio chiavi sono disponibili eseguendo questo comando del terminale:

keytool -list -keystore <path-to-apk-signing-keystore>

Gli hash SHA-256 sono in formato esadecimale delimitato da due punti (91:F7:CB:F9:D6:81…) e i valori origin di Android sono codificati in base64url. Questo esempio Python mostra come convertire il formato hash in un formato esadecimale compatibile, separato da due punti:

import binascii
import base64
fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5'
print("android:apk-key-hash:" + base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))

Sostituisci il valore di fingerprint con il tuo valore. Ecco un risultato di esempio:

android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU

Puoi quindi trovare una corrispondenza per quella stringa come origine consentita sul tuo server. Se disponi di più certificati di firma, ad esempio certificati per il debug e il rilascio, o più app, ripeti la procedura e accetta tutte le origini come valide sul server.

Salvare la password di un utente

Se l'utente fornisce un nome utente e una password per un flusso di autenticazione nella tua app, puoi registrare una credenziale utente che possa essere utilizzata per autenticare l'utente. Per farlo, crea un oggetto CreatePasswordRequest:

Kotlin

fun registerPassword(username: String, password: String) {
    // Initialize a CreatePasswordRequest object.
    val createPasswordRequest =
            CreatePasswordRequest(id = username, password = password)

    // Create credential and handle result.
    coroutineScope.launch {
        try {
            val result =
                credentialManager.createCredential(
                    // Use an activity based context to avoid undefined
                    // system UI launching behavior.
                    activityContext,
                    createPasswordRequest
                  )
            handleRegisterPasswordResult(result)
        } catch (e: CreateCredentialException) {
            handleFailure(e)
        }
    }
}

Java

void registerPassword(String username, String password) {
    // Initialize a CreatePasswordRequest object.
    CreatePasswordRequest createPasswordRequest =
        new CreatePasswordRequest(username, password);

    // Register the username and password.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined
        // system UI launching behavior
        requireActivity(),
        createPasswordRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                handleFailure(e);
            }
        }
    );
}

Assistenza per il recupero delle credenziali

Se un utente non ha più accesso a un dispositivo in cui aveva archiviato le proprie credenziali, potrebbe dover eseguire il ripristino da un backup online sicuro. Per scoprire di più su come supportare questa procedura di recupero delle credenziali, leggi la sezione intitolata "Recupero dell'accesso o aggiunta di nuovi dispositivi" in questo post del blog: Sicurezza delle passkey in Gestore delle password di Google.

Aggiungi il supporto per gli strumenti di gestione delle password con URL noti per gli endpoint delle passkey

Per un'integrazione perfetta e per una compatibilità futura con gli strumenti di gestione delle password e delle credenziali, ti consigliamo di aggiungere il supporto per gli endpoint delle passkey noti. Si tratta di un protocollo aperto che consente alle parti allineate di pubblicizzare formalmente il loro supporto per le passkey e fornire link diretti per la registrazione e la gestione delle passkey.

  1. Per una parte richiedente presso https://example.com, che ha un sito web e app per Android e iOS, l'URL noto sarà https://example.com/.well-known/passkey-endpoints.
  2. Quando viene eseguita una query sull'URL, la risposta deve utilizzare il seguente schema

    {
      "enroll": "https://example.com/account/manage/passkeys/create"
      "manage": "https://example.com/account/manage/passkeys"
    }
    
  3. Per fare in modo che questo link si apra direttamente nell'app anziché sul web, usa i link per app Android.

  4. Ulteriori dettagli sono disponibili nell'URL noto degli endpoint delle passkey su GitHub.

Risolvere gli errori più comuni

La seguente tabella mostra diversi codici di errore e descrizioni comuni e fornisce alcune informazioni sulle loro cause:

Codice di errore e descrizione Motivo
All'inizio dell'accesso non riuscito: 16: il chiamante è stato temporaneamente bloccato a causa di troppe richieste di accesso annullate.

Se riscontri questo periodo di attesa di 24 ore durante lo sviluppo, puoi reimpostarlo cancellando lo spazio di archiviazione delle app di Google Play Services.

In alternativa, per attivare/disattivare questa riduzione su un emulatore o un dispositivo di test, vai all'app Telefono e inserisci il seguente codice: *#*#66382723#*#*. L'app Telefono cancella tutti gli input e potrebbe chiudersi, ma non viene visualizzato un messaggio di conferma.

All'inizio dell'accesso non riuscito: 8: Errore interno sconosciuto.
  1. Sul dispositivo non è stato configurato correttamente l'Account Google.
  2. Il JSON della passkey non viene creato correttamente.
CreatePublicKeyCredentialDomException: impossibile convalidare la richiesta in entrata L'ID pacchetto dell'app non è registrato sul tuo server. Convalidalo nell'integrazione lato server.
CreateCredenzialiSconosciutoEccezione: durante il salvataggio della password è stata trovata una risposta di errore della password con un solo tocco 16: Mancato salvataggio della password perché è probabile che all'utente venga richiesta la compilazione automatica di Android Questo errore si verifica soltanto su Android 13 e versioni precedenti e soltanto se il fornitore della compilazione automatica è Google. In questo caso, gli utenti visualizzeranno una richiesta di salvataggio dalla compilazione automatica e la password viene salvata in Gestore delle password di Google. Tieni presente che le credenziali salvate utilizzando la Compilazione automatica Google vengono condivise in modo bidirezionale con l'API Credential Manager. Di conseguenza, questo errore può essere ignorato.

Risorse aggiuntive

Per scoprire di più sull'API Credential Manager e sulle passkey, consulta le risorse seguenti: