As APIs de transferência de credenciais do Credential Manager permitem a transferência segura de credenciais do usuário entre provedores de credenciais no mesmo dispositivo. Este guia detalha
como os provedores de credenciais no Android podem se integrar às APIs fornecidas pela
androidx.credentials:providerevents biblioteca. Esse recurso oferece suporte a
senhas, chaves de acesso, informações de endereço e campos personalizados usando o
formato de troca de credenciais FIDO (CXF, na sigla em inglês) padronizado.
Principais conceitos
A estrutura de transferência de credenciais facilita a transferência de credenciais ponto a ponto no mesmo dispositivo sem expor credenciais brutas ao SO Android ou a apps não autenticados.
A estrutura define duas funções principais:
- Exportador (provedor de origem) : um provedor de credenciais que atualmente tem credenciais do usuário. Ele pré-registra metadados sobre contas exportáveis
disponíveis (
ExportEntry) com o sistema e responde a solicitações de transferência quando selecionado pelo usuário. - Importador (provedor de cliente ou assistente de configuração): um provedor de credenciais ou
assistente de configuração que inicia uma solicitação de importação
(
ImportCredentialsRequest) especificando os tipos de credenciais e extensões que ele pode receber.
Compatibilidade com a versão do Android
A API Credentials Transfer funciona em dispositivos com o Android 8 (nível 26 da API) e versões mais recentes.
Adicionar dependências
Adicione a dependência androidx.credentials:providerevents ao
build.gradle ou build.gradle.kts do módulo:
dependencies {
implementation("androidx.credentials:providerevents:1.0.0-alpha06")
}
Instanciar a classe necessária
Crie uma instância da ProviderEventsManager necessária.
val providerEventsManager = ProviderEventsManager.create(context)
Implementar o exportador
Para permitir que os usuários exportem credenciais do seu app para outros provedores de credenciais no dispositivo, implemente a função de exportador.
Registrar entradas de exportação
Quando o provedor de credenciais muda (por exemplo, um usuário faz login, adiciona credenciais ou modifica contas), registre ou atualize os itens ExportEntry usando ProviderEventsManager.registerExport().
Cada ExportEntry exige:
id: um identificador de string secreto e gerado aleatoriamente que representa exclusivamente essa entrada de exportação. É necessário manter esse ID em segurança ; ele será necessário mais tarde para verificar as solicitações de transferência recebidas.accountDisplayName: rótulo da conta opcional (por exemplo,"Personal Account").userDisplayName: o identificador principal do usuário (por exemplo,"alice@example.com").icon: um íconeBitmapque representa o provedor ou a conta (escalonado automaticamente para PNG de 32 x 32 pela biblioteca).supportedCredentialTypes: um conjunto de constantes de string deCredentialTypesque representam os tipos que essa entrada contém.
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) } }
Declarar a atividade do exportador no arquivo de manifesto
Quando o usuário seleciona ExportEntry na interface do seletor do sistema, o sistema inicia a Activity de processamento designada. Declare essa Activity com a ação da intent e o esquema de URI de conteúdo obrigatórios:
<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>
Processar a intent de transferência na sua atividade
Na CredentialExportActivity, use IntentHandler.retrieveProviderImportCredentialsRequest(intent) para analisar a solicitação, verificar o app de chamada e o credId, realizar qualquer autenticação biométrica necessária e gravar o payload do FIDO CXF de volta no URI de conteúdo fornecido:
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() } }
Exceções de registro e limpeza de exportação
Todas as exceções nesse módulo são subclasses de
ImportCredentialsException, RegisterExportException ou
ClearExportException.
RegisterExportProviderConfigurationExceptionouClearExportProviderConfigurationException: gerada quando o registro ou a limpeza falham devido a problemas de configuração do provedor (por exemplo,supportedCredentialTypesvazio em umaExportEntryou chamada em uma camada de SO não compatível).RegisterExportUnknownErrorExceptionouClearExportUnknownErrorException: erro de sistema ou armazenamento não classificado ocorreu ao atualizar o registro de exportação.
Implementar o importador
Para importar credenciais para seu app (por exemplo, durante a integração ou a importação do provedor
), implemente a função de importador e inicie o fluxo chamando
ProviderEventsManager.importCredentials().
Criar a solicitação de importação e iniciar o fluxo
Especifique quais CredentialTypes e KnownExtensions o importador
oferece suporte:
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) {}
Exceções de fluxo de importação
Todas as exceções nesse módulo são subclasses de
ImportCredentialsException, RegisterExportException ou
ClearExportException.
ImportCredentialsCancellationException: o usuário dispensou a interface do seletor ou cancelou a atividade de exportação (Activity.RESULT_CANCELED).ImportCredentialsNoExportOptionException: nenhuma entrada de exportação registrada correspondia aos tipos de credenciais solicitados ou o exportador selecionado gerou uma exceção de rejeição.ImportCredentialsProviderConfigurationException: erro de configuração (por exemplo, conjuntocredentialTypesvazio emImportCredentialsRequestou permissões ausentes).ImportCredentialsInvalidJsonException: o exportador retornou um payload JSON malformado ou vazio que falhou na validação da solicitação.ImportCredentialsSystemErrorException: erro interno de transferência do sistema Android ou do Binder ocorreu durante a importação.ImportCredentialsUnknownCallerException: o app de chamada não pôde ser verificado pela estrutura.ImportCredentialsUnknownErrorException: erro não classificado ou inesperado durante o fluxo de importação.
Tipos e extensões de credenciais compatíveis
O objeto androidx.credentials.providerevents.transfer.CredentialTypes define constantes de string padronizadas correspondentes aos tipos de item FIDO CXF:
| Constante | Valor (tipo cxf) |
Descrição |
|---|---|---|
CREDENTIAL_TYPE_BASIC_AUTH |
"basic-auth" |
Credenciais de login de nome de usuário e senha. |
CREDENTIAL_TYPE_PUBLIC_KEY |
"passkey" |
Credenciais de chave pública de chave de acesso FIDO2 ou WebAuthn. |
CREDENTIAL_TYPE_ADDRESS |
"address" |
Informações de endereço postal ou de envio para preenchimento automático de formulários. |
CREDENTIAL_TYPE_API_KEY |
"api-key" |
Chaves e tokens de acesso à API. |
CREDENTIAL_TYPE_CREDIT_CARD |
"credit-card" |
Informações de pagamento com cartão de crédito e débito. |
CREDENTIAL_TYPE_CUSTOM_FIELDS |
"custom-fields" |
Agrupamentos personalizados ou campos definidos pelo usuário. |
CREDENTIAL_TYPE_DRIVERS_LICENSE |
"drivers-license" |
Detalhes da carteira de habilitação. |
CREDENTIAL_TYPE_FILE |
"file" |
Metadados e referências de marcador para arquivos binários. |
CREDENTIAL_TYPE_GENERATED_PASSWORD |
"generated-password" |
Senhas seguras geradas por máquina. |
CREDENTIAL_TYPE_IDENTITY_DOCUMENT |
"identity-document" |
Documentos de identidade nacional, SSN, TIN ou referências de passaporte. |
CREDENTIAL_TYPE_ITEM_REFERENCE |
"item-reference" |
Links lógicos que apontam para outro item no payload. |
CREDENTIAL_TYPE_NOTE |
"note" |
Notas seguras definidas pelo usuário (string UTF-8). |
CREDENTIAL_TYPE_PASSPORT |
"passport" |
Detalhes do documento de viagem do passaporte. |
CREDENTIAL_TYPE_PERSON_NAME |
"person-name" |
Detalhes de identidade e nome da pessoa. |
CREDENTIAL_TYPE_SSH_KEY |
"ssh-key" |
Pares de chaves SSH públicas e privadas. |
CREDENTIAL_TYPE_TOTP |
"totp" |
Secrets de senha única baseada em tempo (2FA). |
CREDENTIAL_TYPE_WIFI |
"wifi" |
SSID e senhas de rede Wi-Fi. |
Matchers WASM (WebAssembly) personalizados (avançado)
Por padrão, chamar RegisterExportRequest.create(context, entries) agrupa o credential_transfer_matcher.wasm padrão do sistema dos recursos da biblioteca, que filtra as entradas com base na interseção de supportedCredentialTypes.
Se o provedor de credenciais exigir uma lógica de correspondência complexa (por exemplo, verificações de capacidade dinâmica ou filtragem condicional com base em campos personalizados), você poderá compilar seu próprio módulo WebAssembly que corresponda à API de transferência de credenciais WASM e transmitir a matriz de bytes bruta diretamente:
// 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)