Le Gestionnaire d'identifiants fait référence à un ensemble d'API introduites dans Android 14 et 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). Lorsque l'API Gestionnaire d'identifiants est appelée, le système Android regroupe les identifiants de tous les fournisseurs d'identifiants installés sur l'appareil. Ce document décrit l'ensemble des API qui fournissent des points de terminaison d'intégration pour ces fournisseurs d'identifiants.
Configuration
Avant d'implémenter une fonctionnalité dans votre fournisseur d'identifiants, suivez la procédure de configuration décrite dans les sections suivantes.
Déclarer des dépendances
Dans le fichier build.gradle
de votre module, déclarez une dépendance à l'aide de la dernière version de la bibliothèque du Gestionnaire d'identifiants :
implementation "androidx.credentials:credentials:1.2.0-{latest}"
Déclarer un élément de service dans le fichier manifeste
Dans le fichier manifeste AndroidManifest.xml
de votre application, ajoutez une déclaration <service>
pour une classe de service qui étend la classe CredentialProviderService
à partir de la bibliothèque androidx.credentials, comme dans l'exemple ci-dessous.
<service android:name=".MyCredentialProviderService"
android:enabled="true"
android:exported="true"
android:label="My Credential Provider"
android:icon="<any drawable icon>"
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE">
<intent-filter>
<action android:name="android.service.credentials.CredentialProviderService"/>
</intent-filter>
<meta-data
android:name="android.credentials.provider"
android:resource="@xml/provider"/>
</service>
L'autorisation et le filtre d'intent présentés ci-dessus sont essentiels pour que le flux du Gestionnaire d'identifiants fonctionne comme prévu. L'autorisation est obligatoire pour que seul le système Android puisse être lié à ce service. Le filtre d'intent permet au Gestionnaire d'identifiants de reconnaître ce service en tant que fournisseur d'identifiants.
Déclarer les types d'identifiants compatibles
Dans le répertoire res/xml
, créez un fichier nommé provider.xml
. Dans ce fichier, déclarez les types d'identifiants pris en charge par votre service. Pour ce faire, utilisez des constantes définies pour chaque type d'identifiant dans la bibliothèque. Dans l'exemple suivant, le service accepte les mots de passe traditionnels ainsi que les clés d'accès, pour lesquels des constantes sont définies en tant que TYPE_PASSWORD_CREDENTIAL
et TYPE_PUBLIC_KEY_CREDENTIAL
:
<?xml version="1.0" encoding="utf-8"?>
<credential-provider xmlns:android="http://schemas.android.com/apk/res/android">
<capabilities>
<capability name="android.credentials.TYPE_PASSWORD_CREDENTIAL" />
<capability name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
</capabilities>
</credential-provider>
Pour ce qui est des niveaux d'API précédents, les fournisseurs d'identifiants s'intègrent à des API, par exemple avec la saisie automatique pour les mots de passe et d'autres données. Ces fournisseurs peuvent utiliser la même infrastructure interne pour stocker les types d'identifiants existants, tout en l'étendant aux autres types compatibles, y compris les clés d'accès.
Approche en deux phases pour les interactions avec les fournisseurs
Le Gestionnaire d'identifiants interagit avec les fournisseurs d'identifiants en deux phases :
- La première phase est la phase de début/de requête au cours de laquelle le système se lie aux services du fournisseur d'identifiants et appelle les méthodes
onBeginGetCredentialRequest()
,onBeginCreateCredentialRequest()
ouonClearCredentialStateRequest()
avec des requêtesBegin…
. Les fournisseurs doivent traiter ces requêtes et répondre avec des réponsesBegin…
, en les renseignant avec des entrées représentant des options visuelles à afficher dans le sélecteur de compte.PendingIntent
doit être défini pour chaque entrée. - Une fois que l'utilisateur a sélectionné une entrée, la phase de sélection commence et le
PendingIntent
associé à l'entrée est déclenché, ce qui génère l'activité du fournisseur correspondant. Une fois que l'utilisateur a terminé d'interagir avec cette activité, le fournisseur d'identifiants doit définir la réponse sur le résultat de l'activité avant de l'achever. Cette réponse est ensuite envoyée à l'application cliente qui a appelé le Gestionnaire d'identifiants.
Gérer la création de clés d'accès
Gérer les requêtes pour la création de clés d'accès
Lorsqu'une application cliente souhaite créer une clé d'accès et la stocker avec un fournisseur d'identifiants, elle appelle l'API createCredential
. Pour gérer cette requête dans votre service de fournisseur d'identifiants et faire en sorte que la clé d'accès soit effectivement stockée dans votre espace de stockage, procédez comme indiqué dans les sections suivantes.
- Remplacez la méthode
onBeginCreateCredentialRequest()
dans votre service à la suite deCredentialProviderService
. - Gérez
BeginCreateCredentialRequest
en créant unBeginCreateCredentialResponse
correspondant et en le transmettant via le rappel. - Lorsque vous créez
BeginCreateCredentialResponse
, ajoutez lesCreateEntries
nécessaires. ChaqueCreateEntry
doit correspondre à un compte dans lequel les identifiants peuvent être enregistrés.PendingIntent
doit également être défini avec les autres métadonnées obligatoires.
L'exemple suivant montre comment mettre en œuvre ces étapes.
override fun onBeginCreateCredentialRequest(
request: BeginCreateCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
) {
val response: BeginCreateCredentialResponse? = processCreateCredentialRequest(request)
if (response != null) {
callback.onResult(response)
} else {
callback.onError(CreateCredentialUnknownException())
}
}
fun processCreateCredentialRequest(request: BeginCreateCredentialRequest): BeginCreateCredentialResponse? {
when (request) {
is BeginCreatePublicKeyCredentialRequest -> {
// Request is passkey type
return handleCreatePasskeyQuery(request)
}
}
// Request not supported
return null
}
private fun handleCreatePasskeyQuery(
request: BeginCreatePublicKeyCredentialRequest
): BeginCreateCredentialResponse {
// Adding two create entries - one for storing credentials to the 'Personal'
// account, and one for storing them to the 'Family' account. These
// accounts are local to this sample app only.
val createEntries: MutableList<CreateEntry> = mutableListOf()
createEntries.add( CreateEntry(
PERSONAL_ACCOUNT_ID,
createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
))
createEntries.add( CreateEntry(
FAMILY_ACCOUNT_ID,
createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
))
return BeginCreateCredentialResponse(createEntries)
}
private fun createNewPendingIntent(accountId: String, action: String): PendingIntent {
val intent = Intent(action).setPackage(PACKAGE_NAME)
// Add your local account ID as an extra to the intent, so that when
// user selects this entry, the credential can be saved to this
// account
intent.putExtra(EXTRA_KEY_ACCOUNT_ID, accountId)
return PendingIntent.getActivity(
applicationContext, UNIQUE_REQ_CODE,
intent, (
PendingIntent.FLAG_MUTABLE
or PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
La création de PendingIntent
doit respecter les directives suivantes :
- L'activité correspondante doit être configurée pour afficher toute invite, confirmation ou sélection biométrique nécessaire
- Toutes les données requises dont le fournisseur a besoin lorsque l'activité correspondante est appelée doivent être définies en tant que données supplémentaires sur l'intent utilisé pour créer
PendingIntent
, par exempleaccountId
dans le flux de création. PendingIntent
doit être créé avec l'indicateurPendingIntent.FLAG_MUTABLE
pour que le système puisse ajouter la requête finale à l'élément supplémentaire sur l'intent.PendingIntent
ne doit pas être créé avec l'indicateurPendingIntent.FLAG_ONE_SHOT
, car l'utilisateur peut sélectionner une entrée, revenir en arrière et la sélectionner à nouveau, ce qui déclencheraitPendingIntent
deux fois.PendingIntent
doit être créé avec un code de requête unique afin que chaque entrée puisse disposer de son proprePendingIntent
correspondant.
Gérer la sélection des entrées pour les requêtes de création de clés d'accès
- Lorsque l'utilisateur sélectionne un
CreateEntry
renseigné précédemment, lePendingIntent
correspondant est appelé et l'Activity
associée du fournisseur est créée. - Une fois la méthode
onCreate
de votre activité appelée, accédez à l'intent associé et transmettez-le à la classePendingIntentHander
pour obtenirProviderCreateCredentialRequest
. - Extrayez
requestJson
,callingAppInfo
etclientDataHash
de la requête. - Extrayez le
accountId
local de l'élément supplémentaire sur l'intent. Il s'agit d'un exemple d'implémentation propre à une application et n'est donc pas obligatoire. Cet ID de compte peut être utilisé pour stocker cet identifiant sur cet ID de compte spécifique - Validez
requestJson
. L'exemple ci-dessous utilise des classes de données locales commePublicKeyCredentialCreationOptions
pour convertir l'entrée JSON en classe structurée conformément à la spécification WebAuthn. En tant que fournisseur d'identifiants, vous pouvez le remplacer par votre propre analyseur. - Vérifiez le lien d'élément pour l'application à l'origine de l'appel si celui-ci provient d'une application Android native.
- Affichez une invite d'authentification. L'exemple ci-dessous utilise l'API biométrique d'Android.
- Une fois l'authentification effectuée, générez
credentialId
et une paire de clés. - Enregistrez la clé privée dans votre base de données locale pour
callingAppInfo.packageName
. - Créez une réponse JSON de l'API Web Authentication comprenant la clé publique et
credentialId
. L'exemple ci-dessous utilise des classes utilitaires locales telles queAuthenticatorAttestationResponse
etFidoPublicKeyCredential
pour vous aider à créer un fichier JSON basé sur les spécifications mentionnées précédemment. En tant que fournisseur d'identifiants, vous pouvez remplacer ces classes par vos propres compilateurs. - Créez
CreatePublicKeyCredentialResponse
avec le fichier JSON généré ci-dessus. - Définissez
CreatePublicKeyCredentialResponse
en tant qu'activité supplémentaire sur unIntent
viaPendingIntentHander.setCreateCredentialResponse()
et définissez cet intent sur le résultat de l'activité. - Terminez l'activité.
L'exemple de code ci-dessous illustre ces étapes. Ce code doit être géré dans la classe d'activité après l'appel de la méthode onCreate()
.
val request =
PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
if (request != null && request.callingRequest is CreatePublicKeyCredentialRequest) {
val publicKeyRequest: CreatePublicKeyCredentialRequest =
request.callingRequest as CreatePublicKeyCredentialRequest
createPasskey(
publicKeyRequest.requestJson,
request.callingAppInfo,
publicKeyRequest.clientDataHash,
accountId
)
}
fun createPasskey(
requestJson: String,
callingAppInfo: CallingAppInfo?,
clientDataHash: ByteArray?,
accountId: String?
) {
val request = PublicKeyCredentialCreationOptions(requestJson)
val biometricPrompt = BiometricPrompt(
this,
<executor>,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
finish()
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
// Generate a credentialId
val credentialId = ByteArray(32)
SecureRandom().nextBytes(credentialId)
// Generate a credential key pair
val spec = ECGenParameterSpec("secp256r1")
val keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(spec)
val keyPair = keyPairGen.genKeyPair()
// Save passkey in your database as per your own implementation
// Create AuthenticatorAttestationResponse object to pass to
// FidoPublicKeyCredential
val response = AuthenticatorAttestationResponse(
requestOptions = request,
credentialId = credentialId,
credentialPublicKey = getPublicKeyFromKeyPair(keyPair),
origin = appInfoToOrigin(callingAppInfo),
up = true,
uv = true,
be = true,
bs = true,
packageName = callingAppInfo.packageName
)
val credential = FidoPublicKeyCredential(
rawId = credentialId, response = response
)
val result = Intent()
val createPublicKeyCredResponse =
CreatePublicKeyCredentialResponse(credential.json())
// Set the CreateCredentialResponse as the result of the Activity
PendingIntentHandler.setCreateCredentialResponse(
result, createPublicKeyCredResponse
)
setResult(Activity.RESULT_OK, result)
finish()
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Use your screen lock")
.setSubtitle("Create passkey for ${request.rp.name}")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG
/* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
)
.build()
biometricPrompt.authenticate(promptInfo)
}
fun appInfoToOrigin(info: CallingAppInfo): String {
val cert = info.signingInfo.apkContentsSigners[0].toByteArray()
val md = MessageDigest.getInstance("SHA-256");
val certHash = md.digest(cert)
// This is the format for origin
return "android:apk-key-hash:${b64Encode(certHash)}"
}
Gérer les requêtes pour la création de mots de passe
Afin de gérer les requêtes pour la création de mots de passe, procédez comme suit :
- Dans la méthode
processCreateCredentialRequest()
mentionnée dans la section précédente, ajoutez un autre boîtier dans le bloc du contacteur pour la gestion des requêtes de mots de passe. - Lorsque vous créez
BeginCreateCredentialResponse
, ajoutez lesCreateEntries
nécessaires. - Chaque
CreateEntry
doit correspondre à un compte dans lesquels les identifiants peuvent être enregistrés. UnPendingIntent
doit être défini avec d'autres métadonnées.
L'exemple suivant montre comment mettre en œuvre ces étapes :
fun processCreateCredentialRequest(
request: BeginCreateCredentialRequest
): BeginCreateCredentialResponse? {
when (request) {
is BeginCreatePublicKeyCredentialRequest -> {
// Request is passkey type
return handleCreatePasskeyQuery(request)
}
is BeginCreatePasswordCredentialRequest -> {
// Request is password type
return handleCreatePasswordQuery(request)
}
}
return null
}
private fun handleCreatePasswordQuery(
request: BeginCreatePasswordCredentialRequest
): BeginCreateCredentialResponse {
val createEntries: MutableList<CreateEntry> = mutableListOf()
// Adding two create entries - one for storing credentials to the 'Personal'
// account, and one for storing them to the 'Family' account. These
// accounts are local to this sample app only.
createEntries.add(
CreateEntry(
PERSONAL_ACCOUNT_ID,
createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
)
)
createEntries.add(
CreateEntry(
FAMILY_ACCOUNT_ID,
createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
)
)
return BeginCreateCredentialResponse(createEntries)
}
Gérer la sélection des entrées pour les requêtes de création de mots de passe
Lorsque l'utilisateur sélectionne un élément CreateEntry
renseigné, le PendingIntent
correspondant s'exécute et affiche l'activité associée. Accédez à l'intent associé transmis dans onCreate
et transmettez-le à la classe PendingIntentHander
pour obtenir la méthode ProviderCreateCredentialRequest
.
L'exemple ci-dessous montre comment mettre en œuvre ce processus. Ce code doit être géré dans la méthode onCreate()
de votre activité.
val createRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
val request: CreatePasswordRequest = createRequest.callingRequest as CreatePasswordRequest
// Fetch the ID and password from the request and save it in your database
<your_database>.addNewPassword(
PasswordInfo(
request.id,
request.password,
createRequest.callingAppInfo.packageName
)
)
//Set the final response back
val result = Intent()
val response = CreatePasswordResponse()
PendingIntentHandler.setCreateCredentialResponse(result, response)
setResult(Activity.RESULT_OK, result)
this@<activity>.finish()
Gérer la connexion des utilisateurs
Pour gérer la connexion des utilisateurs, procédez comme suit :
- Lorsqu'une application cliente tente de connecter un utilisateur, elle prépare une instance
GetCredentialRequest
. - Le framework d'Android propage cette requête à tous les fournisseurs d'identifiants applicables en l'associant à ces services.
- Le service du fournisseur reçoit ensuite un
BeginGetCredentialRequest
contenant une liste deBeginGetCredentialOption
, chacun contenant des paramètres permettant de récupérer les identifiants correspondants.
Pour gérer cette requête dans votre service de fournisseur d'identifiants, procédez comme suit :
Remplacez la méthode
onBeginGetCredentialRequest()
pour gérer la requête. Si vos identifiants sont verrouillés, vous pouvez immédiatement définir unAuthenticationAction
sur la réponse et appeler le rappel.private val unlockEntryTitle = "Authenticate to continue" override fun onBeginGetCredentialRequest( request: BeginGetCredentialRequest, cancellationSignal: CancellationSignal, callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>, ) { if (isAppLocked()) { callback.onResult(BeginGetCredentialResponse( authenticationActions = mutableListOf(AuthenticationAction( unlockEntryTitle, createUnlockPendingIntent()) ) ) ) return } try { response = processGetCredentialRequest(request) callback.onResult(response) } catch (e: GetCredentialException) { callback.onError(GetCredentialUnknownException()) } }
Les fournisseurs qui doivent déverrouiller les identifiants avant de renvoyer des
credentialEntries
doivent configurer un intent en attente qui redirige l'utilisateur vers le flux de déverrouillage de l'application :private fun createUnlockPendingIntent(): PendingIntent { val intent = Intent(UNLOCK_INTENT).setPackage(PACKAGE_NAME) return PendingIntent.getActivity( applicationContext, UNIQUE_REQUEST_CODE, intent, ( PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) ) }
Récupérez les identifiants de votre base de données locale et configurez-les à l'aide de
CredentialEntries
pour les afficher dans le sélecteur. Pour les clés d'accès, vous pouvez définircredentialId
comme élément supplémentaire sur l'intent afin de savoir à quel identifiant il est mappé lorsque l'utilisateur sélectionne cette entrée.companion object { // These intent actions are specified for corresponding activities // that are to be invoked through the PendingIntent(s) private const val GET_PASSKEY_INTENT_ACTION = "PACKAGE_NAME.GET_PASSKEY" private const val GET_PASSWORD_INTENT_ACTION = "PACKAGE_NAME.GET_PASSWORD" } fun processGetCredentialsRequest( request: BeginGetCredentialRequest ): BeginGetCredentialResponse { val callingPackage = request.callingAppInfo?.packageName val credentialEntries: MutableList<CredentialEntry> = mutableListOf() for (option in request.beginGetCredentialOptions) { when (option) { is BeginGetPasswordOption -> { credentialEntries.addAll( populatePasswordData( callingPackage, option ) ) } is BeginGetPublicKeyCredentialOption -> { credentialEntries.addAll( populatePasskeyData( callingPackage, option ) ) ) } else -> { Log.i(TAG, "Request not supported") } } } return BeginGetCredentialResponse(credentialEntries) }
Interrogez les identifiants à partir de votre base de données, créez des entrées de clés d'accès et de mots de passe à renseigner.
private fun populatePasskeyData( callingAppInfo: CallingAppInfo, option: BeginGetPublicKeyCredentialOption ): List<CredentialEntry> { val passkeyEntries: MutableList<CredentialEntry> = mutableListOf() val request = PublicKeyCredentialRequestOptions(option.requestJson) // Get your credentials from database where you saved during creation flow val creds = <getCredentialsFromInternalDb(request.rpId)> val passkeys = creds.passkeys for (passkey in passkeys) { val data = Bundle() data.putString("credId", passkey.credId) passkeyEntries.add( PublicKeyCredentialEntry( context = applicationContext, username = passkey.username, pendingIntent = createNewPendingIntent( GET_PASSKEY_INTENT_ACTION, data ), beginPublicKeyCredentialOption = option, displayName = passkey.displayName, icon = passkey.icon ) ) } return passkeyEntries } // Fetch password credentials and create password entries to populate to // the user private fun populatePasswordData( callingPackage: String, option: BeginGetPasswordOption ): List<CredentialEntry> { val passwordEntries: MutableList<CredentialEntry> = mutableListOf() // Get your password credentials from database where you saved during // creation flow val creds = <getCredentialsFromInternalDb(callingPackage)> val passwords = creds.passwords for (password in passwords) { passwordEntries.add( PasswordCredentialEntry( context = applicationContext, username = password.username, pendingIntent = createNewPendingIntent( GET_PASSWORD_INTENT ), beginGetPasswordOption = option displayName = password.username, icon = password.icon ) ) } return passwordEntries } private fun createNewPendingIntent( action: String, extra: Bundle? = null ): PendingIntent { val intent = Intent(action).setPackage(PACKAGE_NAME) if (extra != null) { intent.putExtra("CREDENTIAL_DATA", extra) } return PendingIntent.getActivity( applicationContext, UNIQUE_REQUEST_CODE, intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) ) }
Après avoir interrogé et renseigné les identifiants, vous devez gérer la phase de sélection pour les identifiants sélectionnés par l'utilisateur, qu'il s'agisse d'une clé d'accès ou d'un mot de passe.
Gérer la sélection des utilisateurs pour les clés d'accès
- Dans la méthode
onCreate
de l'activité correspondante, récupérez l'intent associé et transmettez àPendingIntentHandler.retrieveProviderGetCredentialRequest()
. - Extrayez
GetPublicKeyCredentialOption
de la requête récupérée ci-dessus. Vous devez ensuite extrairerequestJson
etclientDataHash
de cette option. - Extrayez
credentialId
de l'élément supplémentaire sur l'intent renseigné par le fournisseur d'identifiants lors de la configuration duPendingIntent
correspondant. - Extrayez la clé d'accès de votre base de données locale à l'aide des paramètres de requête consultés ci-dessus.
Déclarez que la clé d'accès est valide avec les métadonnées extraites et la vérification de l'utilisateur.
val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val publicKeyRequest = getRequest.credentialOption as GetPublicKeyCredentialOption val requestInfo = intent.getBundleExtra("CREDENTIAL_DATA") val credIdEnc = requestInfo.getString("credId") // Get the saved passkey from your database based on the credential ID // from the publickeyRequest val passkey = <your database>.getPasskey(credIdEnc) // Decode the credential ID, private key and user ID val credId = b64Decode(credIdEnc) val privateKey = b64Decode(passkey.credPrivateKey) val uid = b64Decode(passkey.uid) val origin = appInfoToOrigin(getRequest.callingAppInfo) val packageName = getRequest.callingAppInfo.packageName validatePasskey( publicKeyRequest.requestJson, origin, packageName, uid, passkey.username, credId, privateKey )
Pour valider l'utilisateur, affichez une invite biométrique (ou toute autre méthode d'assertion). L'extrait de code ci-dessous utilise l'API biométrique d'Android.
Une fois l'authentification effectuée, créez une réponse JSON basée sur les spécifications d'assertion de l'authentification Web W3. Dans l'extrait de code ci-dessous, les classes de données d'aide comme
AuthenticatorAssertionResponse
sont utilisées pour intégrer les paramètres structurés et les convertir au format JSON requis. La réponse contient une signature numérique issue de la clé privée d'un identifiant WebAuthn. Le serveur d'un tiers de confiance peut vérifier cette signature pour authentifier un utilisateur avant qu'il ne se connecte.Créez un
PublicKeyCredential
à l'aide du fichier JSON généré ci-dessus et définissez-le sur une réponseGetCredentialResponse
finale. Définissez cette réponse finale sur le résultat de cette activité.
L'exemple suivant montre comment mettre en œuvre ces étapes :
val request = PublicKeyCredentialRequestOptions(requestJson)
val privateKey: ECPrivateKey = convertPrivateKey(privateKeyBytes)
val biometricPrompt = BiometricPrompt(
this,
<executor>,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
finish()
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
val response = AuthenticatorAssertionResponse(
requestOptions = request,
credentialId = credId,
origin = origin,
up = true,
uv = true,
be = true,
bs = true,
userHandle = uid,
packageName = packageName
)
val sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(privateKey)
sig.update(response.dataToSign())
response.signature = sig.sign()
val credential = FidoPublicKeyCredential(
rawId = credId, response = response
)
val result = Intent()
val passkeyCredential = PublicKeyCredential(credential.json)
PendingIntentHandler.setGetCredentialResponse(
result, GetCredentialResponse(passkeyCredential)
)
setResult(RESULT_OK, result)
finish()
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Use your screen lock")
.setSubtitle("Use passkey for ${request.rpId}")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG
/* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
)
.build()
biometricPrompt.authenticate(promptInfo)
Gérer la sélection des utilisateurs pour l'authentification par mot de passe
- Dans l'activité correspondante, accédez à l'intent transmis à
onCreate
et extrayezProviderGetCredentialRequest
à l'aide dePendingIntentHandler
. Utilisez
GetPasswordOption
dans la requête pour récupérer les identifiants du mot de passe pour le nom du package entrant.val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val passwordOption = getRequest.credentialOption as GetPasswordCredentialOption val username = passwordOption.username // Fetch the credentials for the calling app package name val creds = <your_database>.getCredentials(callingAppInfo.packageName) val passwords = creds.passwords val it = passwords.iterator() var password = "" while (it.hasNext() == true) { val passwordItemCurrent = it.next() if (passwordItemCurrent.username == username) { password = passwordItemCurrent.password break } }
Une fois les identifiants récupérés, définissez la réponse pour les identifiants du mot de passe sélectionnés.
// Set the response back val result = Intent() val passwordCredential = PasswordCredential(username, password) PendingIntentHandler.setGetCredentialResponse( result, GetCredentialResponse(passwordCredential) ) setResult(Activity.RESULT_OK, result) finish()
Gérer la sélection d'une entrée d'action d'authentification
Comme indiqué précédemment, un fournisseur d'identifiants peut définir une action AuthenticationAction
si les identifiants sont verrouillés. Si l'utilisateur sélectionne cette entrée, l'activité correspondant à l'action d'intent définie dans PendingIntent
est appelée. Les fournisseurs d'identifiants peuvent ensuite afficher un flux d'authentification biométrique ou un mécanisme similaire pour déverrouiller les identifiants. En cas de réussite, le fournisseur d'identifiants doit créer une réponse BeginGetCredentialResponse
, comme c'est le cas pour la gestion de la connexion des utilisateurs décrite ci-dessous, car les identifiants sont désormais débloqués. Cette réponse doit ensuite être définie via la méthode PendingIntentHandler.setBeginGetCredentialResponse()
avant que l'intent préparé ne soit défini comme résultat et que l'activité ne soit terminée.
Effacer les requêtes d'identifiants
Une application cliente peut demander à ce que tout état conservé pour la sélection des identifiants soit effacé. Par exemple, un fournisseur d'identifiants peut se souvenir des identifiants précédemment sélectionnés et ne les renvoyer que la prochaine fois. Une application cliente appelle cette API et s'attend à ce que la sélection persistante soit effacée. Votre service de fournisseur d'identifiants peut gérer cette requête en ignorant la méthode onClearCredentialStateRequest()
:
override fun onClearCredentialStateRequest(
request: android.service.credentials.ClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>,
) {
// Delete any maintained state as appropriate.
}
Possibilité d'ajouter un lien vers la page des paramètres de votre fournisseur
Pour permettre à vos utilisateurs d'ouvrir les paramètres de votre fournisseur depuis l'écran Mots de passe, clés d'accès et saisie automatique, les applications du fournisseur d'identifiants doivent implémenter l'attribut de fichier manifeste settingsActivity
credential-provider
dans res/xml/provider.xml
. Cet attribut vous permet d'utiliser un intent pour ouvrir l'écran de paramètres de votre application si un utilisateur clique sur le nom d'un fournisseur dans la liste des services Mots de passe, clés d'accès et saisie semi-automatique. Définissez la valeur de cet attribut sur le nom de l'activité à lancer depuis l'écran des paramètres.
<credential-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsSubtitle="Example settings provider name"
android:settingsActivity="com.example.SettingsActivity">
<capabilities>
<capability name="android.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
</capabilities>
</credential-provider>
Intents de paramètres
Ouvrir les paramètres: l'intent android.settings.CREDENTIAL_PROVIDER
affiche un écran de paramètres dans lequel l'utilisateur peut sélectionner ses fournisseurs d'identifiants préférés et supplémentaires.
Service d'identifiants préféré: l'intent ACTION_REQUEST_SET_AUTOFILL_SERVICE
redirige votre utilisateur vers l'écran de sélection du fournisseur préféré. Le fournisseur sélectionné sur cet écran devient le fournisseur d'identifiants et de saisie automatique par défaut.
Obtenir une liste d'autorisation d'applications privilégiées
Les applications privilégiées telles que les navigateurs Web effectuent des appels du Gestionnaire d'identifiants pour le compte d'autres parties de confiance en définissant le paramètre origin
dans les méthodes GetCredentialRequest()
et CreatePublicKeyCredentialRequest()
du Gestionnaire d'identifiants. Pour traiter ces requêtes, le fournisseur d'identifiants récupère l'origin
à l'aide de l'API getOrigin()
.
Pour récupérer l'origin
, l'application du fournisseur d'identifiants doit transmettre une liste d'appelants privilégiés et approuvés à l'API androidx.credentials.provider.CallingAppInfo's getOrigin()
. Cette liste d'autorisation doit être un objet JSON valide. Le origin
est renvoyé si packageName
et les empreintes du certificat obtenues à partir de signingInfo
correspondent à celles d'une application trouvée dans le privilegedAllowlist
transmis à l'API getOrigin()
. Une fois la valeur de l'origin
obtenue, l'application du fournisseur doit considérer cet appel privilégié et définir cette origin
sur les données client dans l'AuthenticatorResponse
, au lieu de calculer l'origin
à l'aide de la signature de l'application appelante.
Si vous récupérez un origin
, utilisez le clientDataHash
fourni directement dans CreatePublicKeyCredentialRequest()
ou GetPublicKeyCredentialOption()
au lieu de l'assemblage et du hachage de clientDataJSON
lors de la requête de signature. Pour éviter les problèmes d'analyse JSON, définissez une valeur d'espace réservé pour clientDataJSON
dans la réponse d'attestation et d'assertion.
Le Gestionnaire de mots de passe de Google utilise une liste d'autorisation ouverte pour les appels vers getOrigin()
. En tant que fournisseur d'identifiants, vous pouvez utiliser cette liste ou fournir la vôtre au format JSON décrit par l'API. C'est au fournisseur de sélectionner la liste à utiliser. Pour obtenir un accès privilégié avec des fournisseurs d'identifiants tiers, reportez-vous à la documentation fournie par le tiers.
Activer les fournisseurs sur un appareil
Les utilisateurs doivent activer le fournisseur sur leur appareil en se rendant dans Paramètres > Mots de passe et comptes > Votre fournisseur > Activer ou Désactiver.
fun createSettingsPendingIntent(): PendingIntent