A API Credential Manager Holder permite que seu app de carteira (também chamado de "carteira") do Android gerencie e apresente credenciais digitais a verificadores.
Principais conceitos
É importante se familiarizar com os seguintes conceitos antes de usar a API Holder.
Formatos de credenciais
As credenciais podem ser armazenadas em apps de carteira digital em diferentes formatos. Esses formatos são especificações de como uma credencial deve ser representada, e cada um contém as seguintes informações sobre ela:
- Tipo:a categoria, como um diploma universitário ou uma carteira de motorista digital.
- Propriedades:atributos como nome e sobrenome.
- Codificação:a forma como a credencial é estruturada, por exemplo, SD-JWT ou mdoc
- Validade:método para verificar criptograficamente a autenticidade da credencial.
Cada formato de credencial faz a codificação e a validação de maneira um pouco diferente, mas funcionalmente eles são iguais.
O registro é compatível com dois formatos:
- SD-JWT:está em conformidade com a especificação de credenciais verificáveis baseadas em SD-JWT (SD-JWT VC) do IETF.
- Documentos móveis ou mdocs:estão em conformidade com a especificação ISO/IEC 18013-5:2021.
Um verificador pode fazer uma solicitação OpenID4VP para SD-JWTs e mdocs ao usar o Credential Manager. A escolha varia de acordo com o caso de uso e o setor.
Registro de metadados de credenciais
O Gerenciador de credenciais não armazena as credenciais de um titular diretamente, mas sim os metadados delas. Um app de suporte precisa primeiro registrar metadados de credenciais
com o Gerenciador de credenciais usando RegistryManager. Esse processo de registro
cria um registro que tem duas finalidades principais:
- Correspondência:os metadados de credenciais registradas são usados para corresponder a futuras solicitações do verificador.
- Tela:os elementos personalizados da interface são mostrados ao usuário na interface do seletor de credenciais.
Você vai usar a classe OpenId4VpRegistry para registrar suas credenciais digitais,
já que ela é compatível com os formatos de credenciais mdoc e SD-JWT. Os verificadores vão enviar
solicitações OpenID4VP para pedir essas credenciais.
Registrar as credenciais do app
Para usar a API Credential Manager Holder, adicione as seguintes dependências ao script de build do módulo do app:
Groovy
dependencies { // Use to implement credentials registrys implementation "androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha04" implementation "androidx.credentials.registry:registry-digitalcredentials-preview:1.0.0-alpha04" implementation "androidx.credentials.registry:registry-provider:1.0.0-alpha04" implementation "androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha04" }
Kotlin
dependencies { // Use to implement credentials registrys implementation("androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha04") implementation("androidx.credentials.registry:registry-digitalcredentials-preview:1.0.0-alpha04") implementation("androidx.credentials.registry:registry-provider:1.0.0-alpha04") implementation("androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha04") }
Criar o RegistryManager
Crie uma instância RegistryManager e registre uma solicitação OpenId4VpRegistry com ela.
// Create the registry manager
val registryManager = RegistryManager.create(context)
// The guide covers how to build this out later
val registryRequest = OpenId4VpRegistry(credentialEntries, id)
try {
registryManager.registerCredentials(registryRequest)
} catch (e: Exception) {
// Handle exceptions
}
Criar uma solicitação OpenId4VpRegistry
Como mencionado anteriormente, você precisa registrar um OpenId4VpRegistry para processar
uma solicitação OpenID4VP de um verificador. Vamos presumir que você tenha alguns tipos de dados locais carregados com suas credenciais da carteira (por exemplo, sdJwtsFromStorage). Agora, você vai convertê-los em nossos equivalentes do Jetpack DigitalCredentialEntry com base no formato deles: SdJwtEntry ou MdocEntry para SD-JWT ou mdoc, respectivamente.
Adicionar Sd-JWTs ao registro
Mapeie cada credencial SD-JWT local para um SdJwtEntry do registro:
fun mapToSdJwtEntries(sdJwtsFromStorage: List<StoredSdJwtEntry>): List<SdJwtEntry> {
val list = mutableListOf<SdJwtEntry>()
for (sdJwt in sdJwtsFromStorage) {
list.add(
SdJwtEntry(
verifiableCredentialType = sdJwt.getVCT(),
claims = sdJwt.getClaimsList(),
entryDisplayPropertySet = sdJwt.toDisplayProperties(),
id = sdJwt.getId() // Make sure this cannot be readily guessed
)
)
}
return list
}
Adicionar mdocs ao registro
Mapeie suas credenciais mdoc locais para o tipo Jetpack MdocEntry:
fun mapToMdocEntries(mdocsFromStorage: List<StoredMdocEntry>): List<MdocEntry> {
val list = mutableListOf<MdocEntry>()
for (mdoc in mdocsFromStorage) {
list.add(
MdocEntry(
docType = mdoc.retrieveDocType(),
fields = mdoc.getFields(),
entryDisplayPropertySet = mdoc.toDisplayProperties(),
id = mdoc.getId() // Make sure this cannot be readily guessed
)
)
}
return list
}
Pontos principais sobre o código
- Uma maneira de configurar o campo
idé registrar um identificador de credencial criptografado para que apenas você possa descriptografar o valor. - Os campos de exibição da interface dos dois formatos precisam ser localizados.
Registrar suas credenciais
Combine as entradas convertidas e registre a solicitação com o
RegistryManager:
val credentialEntries = mapToSdJwtEntries(sdJwtsFromStorage) + mapToMdocEntries(mdocsFromStorage)
val openidRegistryRequest = OpenId4VpRegistry(
credentialEntries = credentialEntries,
id = "my-wallet-openid-registry-v1" // A stable, unique ID to identify your registry record.
)
Agora, vamos registrar suas credenciais com o CredentialManager.
try {
val response = registryManager.registerCredentials(openidRegistryRequest)
} catch (e: Exception) {
// Handle failure
}
Agora você registrou suas credenciais no Gerenciador de credenciais.
Gerenciamento de metadados de apps
Os metadados que o app de carteira registra com o Credential Manager têm as seguintes propriedades:
- Persistência:as informações são salvas localmente e permanecem disponíveis mesmo após reinicializações.
- Armazenamento isolado:os registros de cada app são armazenados separadamente. Isso significa que um app não pode mudar os registros de outro.
- Atualizações com chave:os registros de cada app são identificados por um
id, que permite reidentificar, atualizar ou excluir registros. - Atualização de metadados:é recomendável atualizar os metadados persistentes
sempre que o app mudar ou for carregado pela primeira vez. Se um registro for chamado várias
vezes no mesmo
id, a chamada mais recente vai substituir todos os registros anteriores. Para atualizar, registre novamente sem precisar limpar o registro antigo primeiro.
Opcional: criar um matcher
Um comparador é um binário Wasm que o Gerenciador de credenciais executa em uma sandbox para filtrar suas credenciais registradas em relação a uma solicitação de verificador recebida.
- Matcher padrão:a classe
OpenId4VpRegistryinclui automaticamente o matcherOpenId4VPpadrão (OpenId4VpDefaults.DEFAULT_MATCHER) quando você instancia essa classe. Para todos os casos de uso padrão do OpenID4VP, a biblioteca processa a correspondência para você. - Correspondência personalizada:você só implementaria uma correspondência personalizada se estivesse oferecendo suporte a um protocolo não padrão que exige uma lógica de correspondência própria.
Processar uma credencial selecionada
Quando um usuário seleciona uma credencial, o app de carteira digital precisa processar a solicitação.
Você precisa definir uma atividade que detecte o filtro de intent androidx.credentials.registry.provider.action.GET_CREDENTIAL.
Nossa carteira de exemplo demonstra esse procedimento.
A intent inicia sua atividade com a solicitação do verificador e a origem da chamada,
que você extrai com a
função PendingIntentHandler.retrieveProviderGetCredentialRequest. Isso
retorna um ProviderGetCredentialRequest que contém todas as informações
associadas à solicitação do verificador. Há três componentes principais:
- O app de chamada:o app que fez a solicitação, recuperável com
getCallingAppInfo. - A credencial selecionada:informações sobre qual candidato o usuário escolheu, recuperadas pelo
selectedCredentialSet extension method. Isso vai corresponder ao ID da credencial que você registrou. - Solicitações específicas:a solicitação específica feita pelo verificador, recuperada
do método
getCredentialOptions. Para um fluxo de solicitação de credenciais digitais, você vai encontrar um únicoGetDigitalCredentialOptionnessa lista.
Normalmente, o verificador faz uma solicitação de apresentação de credencial digital, que pode ser processada com o seguinte exemplo de código:
request.credentialOptions.forEach { option ->
if (option is GetDigitalCredentialOption) {
Log.i(TAG, "Got DC request: ${option.requestJson}")
processRequest(option.requestJson)
}
}
Confira um exemplo disso na carteira de exemplo.
Verificar a identidade do verificador
- Extraia o
ProviderGetCredentialRequestda intent:
val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
- Verificar a origem privilegiada:apps privilegiados (como navegadores da Web) podem
fazer chamadas em nome de outros verificadores definindo o parâmetro de origem. Para
recuperar essa origem, transmita uma lista de autores de chamadas privilegiados e confiáveis
(uma lista de permissões no formato JSON) para a API
getOrigin()doCallingAppInfo.
val origin = request?.callingAppInfo?.getOrigin(
privilegedAppsJson // Your allow list JSON
)
Se a origem não estiver vazia:a origem será retornada se o packageName e as
impressões digitais do certificado fornecidas por signingInfo corresponderem às de um app encontrado
na lista de permissões transmitida para a API getOrigin(). Depois que o valor de origem é
recebido, o app do provedor precisa considerar isso como uma chamada privilegiada e definir essa
origem na resposta do OpenID4VP, em vez de calcular a origem usando a
assinatura do app de chamada.
O Gerenciador de senhas do Google usa uma lista de permissões (link em inglês) disponível para chamadas para
getOrigin(). Como provedor de credenciais, use essa lista ou forneça a sua
no formato JSON descrito pela API. Cabe ao provedor selecionar
qual lista será usada. Para ter acesso privilegiado com provedores de credenciais
terceirizados, consulte a documentação fornecida por eles.
Se a origem estiver vazia,a solicitação de verificação será de um app Android. A origem do app a ser colocada na resposta OpenID4VP deve ser calculada como android:apk-key-hash:<encoded SHA 256 fingerprint>.
val appSigningInfo = request?.callingAppInfo?.signingInfoCompat?.signingCertificateHistory[0]?.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val certHash = Base64.encodeToString(md.digest(appSigningInfo), Base64.NO_WRAP or Base64.NO_PADDING)
return "android:apk-key-hash:$certHash"
Renderizar a interface do Holder
Quando uma credencial é selecionada, o app de carteira digital é invocado, guiando o usuário pela interface do app. Há duas maneiras padrão de lidar com esse fluxo de trabalho:
- Se for necessária uma autenticação adicional do usuário para liberar a credencial, use a API BiometricPrompt. Isso é demonstrado na amostra.
- Caso contrário, muitas carteiras optam por um retorno silencioso renderizando uma atividade vazia que passa imediatamente os dados de volta para o app de chamada. Isso minimiza os cliques do usuário e oferece uma experiência mais perfeita.
Retornar a resposta de credencial
Quando o app de contêiner estiver pronto para enviar o resultado de volta, encerre a atividade com a resposta de credencial:
PendingIntentHandler.setGetCredentialResponse(
resultData,
GetCredentialResponse(DigitalCredential(response.responseJson))
)
setResult(RESULT_OK, resultData)
finish()
Se houver uma exceção, envie a exceção de credencial da mesma forma:
PendingIntentHandler.setGetCredentialException(
resultData,
GetCredentialUnknownException() // Configure the proper exception
)
setResult(RESULT_OK, resultData)
finish()
Consulte o app de exemplo para conferir um exemplo completo de como retornar a resposta da credencial no contexto.