Connecter un utilisateur avec le Gestionnaire d'identifiants

Le Gestionnaire d'identifiants est une nouvelle API Jetpack offrant plusieurs méthodes de connexion, dont la combinaison nom d'utilisateur/mot de passe, les clés d'accès et les solutions fédérées (par exemple, Se connecter avec Google) dans une seule API. Elle simplifie donc l'intégration pour les développeurs.

De plus, Credential Manager unifie l'interface de connexion pour toutes les méthodes d'authentification des utilisateurs, qui peuvent ainsi se connecter plus facilement aux applications, quelle que soit la méthode choisie.

Cette page explique le concept des clés d'accès, ainsi que les étapes à suivre pour implémenter la compatibilité côté client des solutions d'authentification, y compris les clés d'accès, à l'aide de l'API Gestionnaire d'identifiants. Vous pouvez également consulter une page de questions fréquentes distincte pour obtenir des réponses plus détaillées et spécifiques.

Vos commentaires sont essentiels pour améliorer l'API Gestionnaire d'identifiants. Pour partager les problèmes que vous rencontrez et nous faire part de vos idées pour améliorer l'API, utilisez le lien suivant :

Envoyer un commentaire

À propos des clés d'accès

Les clés d'accès constituent une option plus sûre et plus facile à utiliser que les mots de passe. Grâce à celles-ci, les utilisateurs peuvent se connecter aux applications et aux sites Web à l'aide d'un capteur biométrique (empreinte digitale ou reconnaissance faciale, par exemple), d'un code ou d'un schéma. Il s'agit d'un moyen permettant aux utilisateurs de se connecter facilement, sans avoir à mémoriser des noms d'utilisateur ni des mots de passe.

Les clés d'accès s'appuient sur la norme WebAuthn (Web Authentication) développée conjointement par FIDO Alliance et W3C (World Wide Web Consortium). WebAuthn authentifie les utilisateurs à l'aide de la cryptographie à clé publique. Le site Web ou l'application à laquelle l'utilisateur se connecte peut voir et stocker la clé publique, mais jamais la clé privée. La clé privée reste secrète et sécurisée. Étant donné que la clé est unique et associée au site Web ou à l'application, les clés d'accès ne peuvent pas être obtenues par hameçonnage, ce qui renforce la sécurité.

Le Gestionnaire d'identifiants permet aux utilisateurs de créer des clés d'accès et de les stocker dans le Gestionnaire de mots de passe de Google.

Conditions préalables

Pour utiliser le Gestionnaire d'identifiants, suivez les étapes décrites dans cette section.

Utiliser une version récente de la plate-forme

Le Gestionnaire d'identifiants est compatible avec Android 4.4 (niveau d'API 19) et versions ultérieures.

Ajouter des dépendances à votre application

Ajoutez les dépendances suivantes au script de compilation du module de votre application :

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"
}

Préserver les classes dans le fichier ProGuard

Dans le fichier proguard-rules.pro de votre module, ajoutez les directives suivantes :

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

Découvrez comment réduire, obscurcir et optimiser votre application.

Ajouter la prise en charge de Digital Asset Links

Afin d'activer la prise en charge des clés d'accès pour votre application Android, associez votre application à un site Web qui vous appartient. Pour déclarer cette association, procédez comme suit :

  1. Créez un fichier JSON Digital Asset Links. Par exemple, pour déclarer que le site Web https://signin.example.com et une application Android ayant le nom de package com.example peuvent partager les identifiants de connexion, créez un fichier nommé assetlinks.json avec le contenu suivant :

    [
      {
        "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
          ]
        }
      }
    ]
    

    Le champ relation est un tableau d'une ou de plusieurs chaînes décrivant la relation déclarée. Pour déclarer que les applications et les sites partagent des identifiants de connexion, spécifiez les relations en tant que delegate_permission/handle_all_urls et delegate_permission/common.get_login_creds.

    Le champ target est un objet qui spécifie l'élément auquel la déclaration s'applique. Les champs suivants identifient un site Web :

    namespace web
    site

    URL du site Web, au format https://domain[:optional_port]. (par exemple, https://www.example.com).

    La propriété domain doit être complète, et optional_port doit être omis si vous utilisez le port 443 pour HTTPS.

    Une cible site ne peut être qu'un domaine racine : vous ne pouvez pas limiter une association d'applications à un sous-répertoire spécifique. N'incluez pas de chemin d'accès dans l'URL, comme une barre oblique finale.

    Les sous-domaines ne sont pas considérés comme identiques : si vous spécifiez domain comme www.example.com, le domaine www.counter.example.com ne sera pas associé à votre application.

    Les champs suivants identifient une application Android :

    namespace android_app
    package_name Nom de package déclaré dans le fichier manifeste de l'application. Exemple : com.example.android
    sha256_cert_fingerprints Empreinte SHA256 du certificat de signature de votre application.
  2. Hébergez le fichier JSON Digital Assets Links à l'emplacement suivant sur le domaine de connexion :

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

    Par exemple, si votre domaine de connexion est signin.example.com, hébergez le fichier JSON sur https://signin.example.com/.well-known/assetlinks.json.

    Le fichier MIME du fichier Digital Asset Links doit être au format JSON. Assurez-vous que le serveur envoie un en-tête Content-Type: application/json dans la réponse.

  3. Assurez-vous que votre hôte autorise Google à récupérer votre fichier Digital Asset Links. Si vous avez un fichier robots.txt, il doit permettre à l'agent Googlebot de récupérer /.well-known/assetlinks.json. La plupart des sites peuvent autoriser n'importe quel agent automatisé à récupérer les fichiers du chemin /.well-known/ afin que d'autres services puissent accéder aux métadonnées de ces fichiers :

    User-agent: *
    Allow: /.well-known/
    
  4. Ajoutez la ligne suivante au fichier manifeste sous <application> :

    <meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
    
  5. Si vous utilisez la connexion par mot de passe via le Gestionnaire d'identifiants, procédez comme suit pour configurer la liaison de ressources numériques dans le fichier manifeste. Cette étape n'est pas requise si vous n'utilisez que des clés d'accès.

    Déclarez l'association dans l'application Android. Ajoutez un objet qui spécifie les fichiers assetlinks.json à charger. Vous devez échapper les apostrophes et les guillemets que vous utilisez dans la chaîne. Par exemple :

    <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
    

Configurer le Gestionnaire d'identifiants

Pour configurer et initialiser un objet CredentialManager, ajoutez une logique semblable à celle-ci :

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)

Indiquer des champs d'identifiants

Sous Android 14 ou version ultérieure, l'attribut isCredential peut être utilisé pour indiquer des champs d'identifiants, tels que des champs de nom d'utilisateur ou de mot de passe. Cet attribut indique que cette vue est un champ d'identifiant destiné à fonctionner avec le Gestionnaire d'identifiants et les fournisseurs d'identifiants tiers, tout en permettant aux services de saisie automatique de fournir de meilleures suggestions de saisie automatique. Lorsque l'application utilise l'API Gestionnaire d'identifiants, la bottom sheet du Gestionnaire d'identifiants avec les identifiants disponibles s'affiche, et il n'est plus nécessaire d'afficher la boîte de dialogue de saisie automatique pour le nom d'utilisateur ou le mot de passe.

Pour utiliser l'attribut isCredential, ajoutez-le aux vues concernées :

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

Connecter un utilisateur

Pour récupérer toutes les clés d'accès et options de mots de passe associées au compte de l'utilisateur, procédez comme suit :

  1. Initialisez les options d'authentification avec mot de passe et clé d'accès :

    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. Utilisez les options récupérées à l'étape précédente pour créer la requête de connexion.

    Kotlin

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

    Java

    GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder()
        .addCredentialOption(getPasswordOption)
        .addCredentialOption(getPublicKeyCredentialOption)
        .build();
  3. Lancez le flux de connexion :

    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'exemple suivant montre comment mettre en forme la requête JSON lorsque vous obtenez une clé d'accès :

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

L'exemple suivant montre comment une réponse JSON peut se présenter après l'obtention d'identifiants de clé publique :

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

Gérer les exceptions lorsqu'aucun identifiant n'est disponible

Dans certains cas, il se peut qu'aucun identifiant ne soit disponible ou que l'utilisateur n'autorise pas l'utilisation d'un tel identifiant. Si getCredential() est appelé et qu'aucun identifiant n'est trouvé, NoCredentialException est renvoyée. Dans ce cas, votre code doit gérer les instances 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);
}

Sur Android 14 ou version ultérieure, vous pouvez réduire la latence lorsque vous affichez le sélecteur de compte à l'aide de la méthode prepareGetCredential() avant d'appeler getCredential().

Kotlin

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

Java

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

La méthode prepareGetCredential() n'appelle pas d'éléments d'interface utilisateur. Elle vous aide uniquement à effectuer le travail de préparation afin que vous puissiez lancer ultérieurement l'opération de récupération des identifiants restante (qui implique les interfaces utilisateur) via l'API getCredential().

Les données mises en cache sont renvoyées dans un objet PrepareGetCredentialResponse. S'il existe des identifiants, les résultats sont mis en cache. Vous pourrez ensuite lancer l'API getCredential() restante pour afficher le sélecteur de compte avec les données mises en cache.

Flux d'inscription

Vous pouvez enregistrer un utilisateur à authentifier, soit à l'aide d'une clé d'accès, soit avec un mot de passe.

Créer une clé d'accès

Pour permettre aux utilisateurs d'enregistrer une clé d'accès et de l'utiliser pour la réauthentification, enregistrez des identifiants utilisateur à l'aide d'un objet 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());
                }
            }
        }
    );
}

Mettre en forme la requête JSON

Après avoir créé une clé d'accès, vous devez l'associer au compte de l'utilisateur et stocker sa clé publique sur votre serveur. L'exemple de code suivant montre comment mettre en forme la requête JSON lorsque vous créez une clé d'accès.

Cet article de blog explique comment appliquer une authentification simplifiée à vos applications et comment mettre en forme une requête JSON lorsque vous créez et utilisez des clés d'accès pour l'authentification. Il explique également pourquoi les mots de passe ne constituent pas une solution d'authentification efficace, comment exploiter les identifiants biométriques existants, comment associer votre application à un site Web dont vous êtes propriétaire, comment créer des clés d'accès et comment les utiliser pour s'authentifier.

{
  "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"
  }
}

Définir des valeurs pour "authenticatorAttachment"

Le paramètre authenticatorAttachment ne peut être défini qu'au moment de la création des identifiants. Vous pouvez spécifier platform, cross-platform ou aucune valeur. Dans la plupart des cas, aucune valeur n'est recommandée.

  • platform : pour enregistrer l'appareil actuel de l'utilisateur ou inviter un utilisateur de mot de passe à passer à des clés d'accès après une connexion, définissez authenticatorAttachment sur platform.
  • cross-platform : cette valeur est couramment utilisée lors de l'enregistrement d'identifiants multifacteur. Elle n'est pas utilisée dans un contexte de clé d'accès.
  • Aucune valeur : pour permettre aux utilisateurs de créer des clés d'accès sur leurs appareils préférés (par exemple, dans les paramètres de compte), le paramètre authenticatorAttachment ne doit pas être spécifié lorsqu'un l’utilisateur choisit d’ajouter une clé d’accès. Dans la plupart des cas, il est préférable de ne pas spécifier le paramètre.

Empêcher la création de clés d'accès en double

Répertoriez les ID d'identification dans le tableau excludeCredentials facultatif pour empêcher la création d'une clé d'accès s'il en existe déjà une avec le même fournisseur.

Gérer la réponse JSON

L'extrait de code suivant présente un exemple de réponse JSON visant à créer des identifiants de clé publique. Découvrez comment gérer les identifiants de clé publique obtenus.

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

Vérifier l'origine de la requête JSON de données client

L'origin représente l'application ou le site Web d'où provient une requête. Les clés d'accès l'utilisent pour se protéger contre les attaques par hameçonnage. Le serveur de votre application doit vérifier l'origine des données client par rapport à une liste d'autorisation d'applications et de sites Web approuvés. Si le serveur reçoit une requête d'une application ou d'un site Web dont l'origine n'est pas reconnue, la requête doit être rejetée.

Dans le cas du Web, origin reflète l'origine du même site où l'identifiant a été connecté. Par exemple, pour l'URL https://www.example.com:8443/store?category=shoes#athletic, l'origin correspond à https://www.example.com:8443.

Pour les applications Android, le user-agent définit automatiquement origin sur la signature de l'application appelante. Cette signature doit être vérifiée en tant que correspondance sur votre serveur pour valider l'appelant de l'API de clé d'accès. L'origin Android est un URI dérivé du hachage SHA-256 du certificat de signature d'APK, par exemple :

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

Vous pouvez obtenir les hachages SHA-256 des certificats de signature d'un keystore en exécutant la commande de terminal suivante :

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

Les hachages SHA-256 sont au format hexadécimal délimité par des deux-points (91:F7:CB:F9:D6:81…), et les valeurs Android origin sont encodées en base64url. Cet exemple Python montre comment convertir le format de hachage dans un format hexadécimal compatible, séparé par des deux-points :

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('=', ''))

Remplacez la valeur de fingerprint par votre propre valeur. Voici un exemple de résultat :

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

Vous pouvez ensuite faire correspondre cette chaîne en tant qu'origine autorisée sur votre serveur. Si vous disposez de plusieurs certificats de signature, tels que des certificats de débogage et de publication, ou de plusieurs applications, répétez le processus et acceptez toutes ces origines comme valides sur le serveur.

Enregistrer le mot de passe d'un utilisateur

Si l'utilisateur fournit un nom d'utilisateur et un mot de passe pour un flux d'authentification dans votre application, vous pouvez enregistrer des identifiants utilisateur permettant d'authentifier cet utilisateur. Pour ce faire, créez un objet 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);
            }
        }
    );
}

Prendre en charge la récupération des identifiants

Si un utilisateur n'a plus accès à un appareil sur lequel il avait stocké ses identifiants, il devra peut-être effectuer une récupération à partir d'une sauvegarde en ligne sécurisée. Pour en savoir plus sur la prise en charge de ce processus de récupération d'identifiants, consultez la section intitulée "Récupérer l'accès ou ajouter des appareils" dans cet article de blog : Sécurité des clés d'accès dans le Gestionnaire de mots de passe de Google.

Ajouter la prise en charge des outils de gestion des mots de passe grâce à l'URL well-known pour les points de terminaison de clé d'accès

Pour une intégration parfaite et une compatibilité future avec les outils de gestion des mots de passe et des identifiants, nous vous recommandons d'ajouter la prise en charge des URL well-known pour les points de terminaison de clé d'accès. Il s'agit d'un protocole ouvert qui permet aux parties qui l'adoptent de promouvoir officiellement leur compatibilité avec les clés d'accès et de fournir des liens directs pour l'enregistrement et la gestion des clés d'accès.

  1. Pour un tiers de confiance sur https://example.com, qui dispose d'un site Web et d'applications Android et iOS, l'URL well-known est https://example.com/.well-known/passkey-endpoints.
  2. Lorsque l'URL est interrogée, la réponse doit utiliser le schéma suivant :

    {
      "enroll": "https://example.com/account/manage/passkeys/create"
      "manage": "https://example.com/account/manage/passkeys"
    }
    
  3. Pour que ce lien s'ouvre directement dans votre application plutôt que dans le navigateur Web, utilisez des Android App Links.

  4. Pour en savoir plus, consultez l'explication sur l'URL well-known pour les points de terminaison de clé d'accès sur GitHub.

Résoudre les erreurs courantes

Le tableau suivant présente plusieurs descriptions et codes d'erreur courants, ainsi que certaines informations sur leurs causes :

Code d'erreur et description Cause
"On Begin Sign In Failure: 16" : l'appelant a été temporairement bloqué en raison d'un trop grand nombre d'invites de connexion annulées.

Si vous rencontrez cette erreur imposant un délai de 24 heures avant toute nouvelle connexion pendant le développement, vous pouvez l'annuler en effaçant le stockage des applications des services Google Play.

Si vous souhaitez désactiver ce délai sur un appareil de test ou un émulateur, vous pouvez également accéder à l'application Téléphone et saisir le code suivant : *#*#66382723#*#*. L'application Téléphone efface toutes les entrées et peut se fermer, mais aucun message de confirmation ne s'affiche.

"On Begin Sign In Failure: 8" : erreur interne inconnue.
  1. L'appareil n'est pas configuré correctement avec le compte Google.
  2. Le fichier JSON de clé d'accès n'est pas créé correctement.
"CreatePublicKeyCredentialDomException" : la requête entrante ne peut pas être validée L'ID de package de l'application n'est pas enregistré auprès de votre serveur. Vérifiez cela dans votre intégration côté serveur.
CreateCredentialUnknownException : lors de l'enregistrement du mot de passe, One Tap 16 indique qu'aucun mot de passe n'a été trouvé. L'enregistrement du mot de passe est ignoré, car l'utilisateur est probablement invité à utiliser la saisie automatique Android. Cette erreur ne se produit que sous Android 13 ou version antérieure, et uniquement si Google est le fournisseur de saisie automatique. Dans ce cas, une invite d'enregistrement de la saisie automatique s'affiche, et le mot de passe est enregistré dans le Gestionnaire de mots de passe de Google. Notez que les identifiants enregistrés à l'aide de la saisie automatique avec Google sont partagés de manière bidirectionnelle avec l'API Gestionnaire d'identifiants. Cette erreur peut donc être ignorée.

Ressources supplémentaires

Pour en savoir plus sur l'API Gestionnaire d'identifiants et sur les clés d'accès, consultez les ressources suivantes :