Les API de transfert d'identifiants du gestionnaire d'identifiants permettent de transférer de manière sécurisée les identifiants utilisateur entre les fournisseurs d'identifiants sur le même appareil. Ce guide explique
comment les fournisseurs d'identifiants sur Android peuvent s'intégrer aux API fournies par la
androidx.credentials:providerevents bibliothèque. Cette fonctionnalité est compatible avec les
mots de passe, les clés d'accès, les informations d'adresse et les champs personnalisés à l'aide du
format d'échange d'identifiants FIDO (CXF) standardisé.
Concepts fondamentaux
Le framework de transfert d'identifiants facilite le transfert d'identifiants peer-to-peer sur le même appareil sans exposer les identifiants bruts à l'OS Android ni aux applications non authentifiées.
Le framework définit deux rôles principaux :
- Exportateur (fournisseur source) : fournisseur d'identifiants qui détient actuellement les identifiants utilisateur. Il pré-enregistre les métadonnées sur les comptes exportables
disponibles (
ExportEntry) auprès du système et répond aux demandes de transfert lorsqu'il est sélectionné par l'utilisateur. - Importateur (fournisseur client ou assistant de configuration) : fournisseur d'identifiants ou
assistant de configuration qui lance une demande d'importation
(
ImportCredentialsRequest) spécifiant les types d'identifiants et d'extensions qu'il peut recevoir.
Compatibilité avec les versions d'Android
L'API de transfert d'identifiants fonctionne sur les appareils équipés d'Android 8 (niveau d'API 26) ou version ultérieure.
Ajouter des dépendances
Ajoutez la dépendance androidx.credentials:providerevents à
build.gradle ou build.gradle.kts de votre module :
dependencies {
implementation("androidx.credentials:providerevents:1.0.0-alpha06")
}
Instancier la classe requise
Créez une instance de ProviderEventsManager requise.
val providerEventsManager = ProviderEventsManager.create(context)
Implémenter l'exportateur
Pour permettre aux utilisateurs d'exporter des identifiants de votre application vers d'autres fournisseurs d'identifiants sur l'appareil, implémentez le rôle d'exportateur.
Enregistrer les entrées d'exportation
Lorsque votre fournisseur d'identifiants change (par exemple, lorsqu'un utilisateur se connecte, ajoute des identifiants ou modifie des comptes), enregistrez ou mettez à jour vos éléments ExportEntry à l'aide de ProviderEventsManager.registerExport().
Chaque ExportEntry nécessite les éléments suivants :
id: identifiant de chaîne secret généré de manière aléatoire qui représente de manière unique cette entrée d'exportation. Vous devez conserver cet ID de manière sécurisée ; vous en aurez besoin ultérieurement pour vérifier les demandes de transfert entrantes.accountDisplayName: libellé de compte facultatif (par exemple,"Personal Account").userDisplayName: identifiant principal de l'utilisateur (par exemple,"alice@example.com").icon: icôneBitmapreprésentant le fournisseur ou le compte (mise à l'échelle automatique au format PNG 32x32 par la bibliothèque).supportedCredentialTypes: ensemble de constantes de chaîne deCredentialTypesreprésentant les types contenus dans cette entrée.
suspend fun registerMyProviderForExport( providerEventsManager: ProviderEventsManager, providerIcon: Bitmap, // Randomly generated and stored in encrypted storage secretEntryId: String ) { val entry = ExportEntry( id = secretEntryId, accountDisplayName = "MyProvider Personal", userDisplayName = "alice@example.com", icon = providerIcon, supportedCredentialTypes = setOf( CredentialTypes.CREDENTIAL_TYPE_BASIC_AUTH, // Passwords CredentialTypes.CREDENTIAL_TYPE_PUBLIC_KEY, // Passkeys CredentialTypes.CREDENTIAL_TYPE_ADDRESS, CredentialTypes.CREDENTIAL_TYPE_CREDIT_CARD ) ) // RegisterExportRequest.create() attaches the default WASM matcher from assets val request = RegisterExportRequest.create(context, listOf(entry)) try { val response = providerEventsManager.registerExport(request) // Registration successful } catch (e: Exception) { // Handle registration exceptions (e.g., RegisterExportProviderConfigurationException) } }
Déclarer l'activité de l'exportateur dans le fichier manifeste
Lorsque l'utilisateur sélectionne votre ExportEntry dans l'interface utilisateur du sélecteur système, le système lance votre Activity de gestion désignée. Déclarez cette activité avec l'action d'intent obligatoire et le schéma d'URI de contenu :
<activity
android:name="com.example.CredentialExportActivity"
android:exported="true"
android:label="@string/export_activity_label">
<intent-filter>
// This intent action is required for Credential Manager to invoke this activity
<action android:name="androidx.identitycredentials.action.IMPORT_CREDENTIALS" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
</intent-filter>
</activity>
Gérer l'intent de transfert dans votre activité
Dans votre CredentialExportActivity, utilisez IntentHandler.retrieveProviderImportCredentialsRequest(intent) pour analyser la requête, vérifier l'application appelante et credId, effectuer toute authentification biométrique requise et réécrire la charge utile FIDO CXF dans l'URI de contenu fourni :
class CredentialExportActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 1. Extract the transfer request from the incoming Intent val request: ProviderImportCredentialsRequest? = IntentHandler.retrieveProviderImportCredentialsRequest(intent) if (request == null) { finishWithError() return } // 2. Validate CallingAppInfo and secret `credId` val callingAppPackage = request.callingAppInfo.packageName val receivedCredId = request.credId if (!verifySecretEntryId(receivedCredId) || !isTrustedImporter(callingAppPackage)) { // Secret ID mismatch or untrusted caller -> abort sendExceptionAndFinish(ImportCredentialsNoExportOptionException("Unauthorized request")) return } // 3. Optional: Prompt user for Biometric / PIN authentication before exporting authenticateUserThenExport(request) } private fun authenticateUserThenExport(request: ProviderImportCredentialsRequest) { // ... Biometric prompt logic ... // Once authenticated, generate the FIDO CXF JSON string matching the requested types val cxfJsonPayload = buildFidoCxfJsonPayload( requestedTypes = request.request.credentialTypes, requestedExtensions = request.request.knownExtensions ) val response = ImportCredentialsResponse(cxfJsonPayload) // 4. Write the JSON payload to the Content URI and set Activity result IntentHandler.setImportCredentialsResponse( context = this, uri = request.uri, intent = intent, response = response ) setResult(Activity.RESULT_OK, intent) finish() } private fun sendExceptionAndFinish(exception: androidx.credentials.providerevents.exception.ImportCredentialsException) { IntentHandler.setImportCredentialsException(intent, exception) setResult(Activity.RESULT_OK, intent) finish() } private fun verifySecretEntryId(credentialId: String): Boolean { // Check if credentialId matches what you stored when calling RegisterExportRequest return credentialId == getStoredSecretEntryId() } private fun isTrustedImporter(packageName: String): Boolean { // Implement any specific allowlisting / caller checks if required return true } private fun finishWithError() { setResult(Activity.RESULT_CANCELED) finish() } }
Exceptions d'enregistrement et d'effacement d'exportation
Toutes les exceptions de ce module sont des sous-classes de
ImportCredentialsException, RegisterExportException ou
ClearExportException.
RegisterExportProviderConfigurationExceptionouClearExportProviderConfigurationException: générées lorsque l'enregistrement ou l'effacement échoue en raison de problèmes de configuration du fournisseur (par exemple,supportedCredentialTypesvide dans unExportEntryou appel sur un niveau de système d'exploitation non compatible).RegisterExportUnknownErrorExceptionouClearExportUnknownErrorException: erreur de système ou de stockage non classée survenue lors de la mise à jour du registre d'exportation.
Implémenter l'importateur
Pour importer des identifiants dans votre application (par exemple, lors de l'intégration ou de l'importation de fournisseurs), implémentez le rôle d'importateur et lancez le flux en appelant
ProviderEventsManager.importCredentials().
Construire la requête d'importation et lancer le flux
Spécifiez les CredentialTypes et KnownExtensions compatibles avec votre importateur
:
suspend fun startCredentialImport( activityContext: Context, providerEventsManager: ProviderEventsManager ) { val importRequest = ImportCredentialsRequest( credentialTypes = setOf( CredentialTypes.CREDENTIAL_TYPE_BASIC_AUTH, CredentialTypes.CREDENTIAL_TYPE_PUBLIC_KEY, CredentialTypes.CREDENTIAL_TYPE_ADDRESS, CredentialTypes.CREDENTIAL_TYPE_NOTE ), knownExtensions = setOf( KnownExtensions.KNOWN_EXTENSION_SHARED ) ) try { // Launches the system Selector UI; suspends until user selects a provider and completes transfer val response = providerEventsManager.importCredentials(activityContext, importRequest) // 1. Inspect the source exporter's package info val exporterPackageName = response.callingAppInfo.packageName // 2. Parse the FIDO CXF JSON string val cxfJsonString = response.response.responseJson parseAndSaveImportedCredentials(cxfJsonString) } catch (e: ImportCredentialsException) { // Handle specific import exceptions (e.g., ImportCredentialsCancellationException) handleImportFailure(e) } } private fun parseAndSaveImportedCredentials(cxfJsonString: String) { val rootJson = JSONObject(cxfJsonString) // Parse according to FIDO Credential Exchange Format (CXF v1.0) specification: // https://fidoalliance.org/specs/cx/cxf-v1.0-ps-20250814.html } // Helper function to make it compile private fun handleImportFailure(e: ImportCredentialsException) {}
Exceptions de flux d'importation
Toutes les exceptions de ce module sont des sous-classes de
ImportCredentialsException, RegisterExportException ou
ClearExportException.
ImportCredentialsCancellationException: l'utilisateur a fermé l'interface utilisateur du sélecteur ou a annulé l'activité d'exportation (Activity.RESULT_CANCELED).ImportCredentialsNoExportOptionException: aucune entrée d'exportation enregistrée ne correspond aux types d'identifiants demandés, ou l'exportateur sélectionné a généré une exception de refus.ImportCredentialsProviderConfigurationException: erreur de configuration (par exemple, ensemblecredentialTypesvide dansImportCredentialsRequestou autorisations manquantes).ImportCredentialsInvalidJsonException: l'exportateur a renvoyé une charge utile JSON mal formée ou vide qui n'a pas réussi la validation de la requête.ImportCredentialsSystemErrorException: erreur interne du système Android ou de transfert Binder survenue lors de l'importation.ImportCredentialsUnknownCallerException: l'application appelante n'a pas pu être validée par le framework.ImportCredentialsUnknownErrorException: erreur non classée ou inattendue lors du flux d'importation.
Types d'identifiants et extensions compatibles
L'objet androidx.credentials.providerevents.transfer.CredentialTypes définit des constantes de chaîne standardisées correspondant aux types d'éléments FIDO CXF :
| Constante | Valeur (type cxf) |
Description |
|---|---|---|
CREDENTIAL_TYPE_BASIC_AUTH |
"basic-auth" |
Identifiants de connexion (nom d'utilisateur et mot de passe). |
CREDENTIAL_TYPE_PUBLIC_KEY |
"passkey" |
Identifiants de clé publique de clé d'accès FIDO2 ou WebAuthn. |
CREDENTIAL_TYPE_ADDRESS |
"address" |
Informations sur l'adresse postale ou de livraison pour le remplissage automatique des formulaires. |
CREDENTIAL_TYPE_API_KEY |
"api-key" |
Clés et jetons d'accès à l'API. |
CREDENTIAL_TYPE_CREDIT_CARD |
"credit-card" |
Informations de paiement par carte de crédit et de débit. |
CREDENTIAL_TYPE_CUSTOM_FIELDS |
"custom-fields" |
Groupements personnalisés ou champs définis par l'utilisateur. |
CREDENTIAL_TYPE_DRIVERS_LICENSE |
"drivers-license" |
Informations sur le permis de conduire. |
CREDENTIAL_TYPE_FILE |
"file" |
Métadonnées et références d'espace réservé pour les fichiers binaires. |
CREDENTIAL_TYPE_GENERATED_PASSWORD |
"generated-password" |
Mots de passe sécurisés générés par une machine. |
CREDENTIAL_TYPE_IDENTITY_DOCUMENT |
"identity-document" |
Références de carte nationale d'identité, de numéro de sécurité sociale, de numéro d'identification fiscale ou de passeport. |
CREDENTIAL_TYPE_ITEM_REFERENCE |
"item-reference" |
Liens logiques pointant vers un autre élément de la charge utile. |
CREDENTIAL_TYPE_NOTE |
"note" |
Notes sécurisées définies par l'utilisateur (chaîne UTF-8). |
CREDENTIAL_TYPE_PASSPORT |
"passport" |
Informations sur le document de voyage (passeport). |
CREDENTIAL_TYPE_PERSON_NAME |
"person-name" |
Informations sur l'identité et le nom d'une personne. |
CREDENTIAL_TYPE_SSH_KEY |
"ssh-key" |
Paires de clés publique et privée SSH. |
CREDENTIAL_TYPE_TOTP |
"totp" |
Secrets de mot de passe à usage unique basé sur le temps (2FA). |
CREDENTIAL_TYPE_WIFI |
"wifi" |
SSID et phrases secrètes du réseau Wi-Fi. |
Correspondances WASM (WebAssembly) personnalisées (avancé)
Par défaut, l'appel de RegisterExportRequest.create(context, entries) regroupe le credential_transfer_matcher.wasm par défaut du système à partir des éléments de la bibliothèque, qui filtre les entrées uniquement en fonction de l'intersection de supportedCredentialTypes.
Si votre fournisseur d'identifiants nécessite une logique de correspondance complexe (par exemple, des vérifications de capacités dynamiques ou un filtrage conditionnel basé sur des champs personnalisés), vous pouvez compiler votre propre module WebAssembly correspondant à l'API de transfert d'identifiants WASM et transmettre directement le tableau d'octets bruts :
// Loading a custom WASM matcher val customMatcherBytes = context.assets.open("my_custom_matcher.wasm").use { it.readBytes() } val customRequest = RegisterExportRequest( entries = myEntries, exportMatcher = customMatcherBytes ) providerEventsManager.registerExport(customRequest)