Habilidades do Android
Conferir no GitHubRecuperar e-mail verificado
android skills add verified-emailVisão geral
Este guia descreve como implementar a recuperação de e-mail verificado usando a API Digital Credentials Verifier por meio de uma solicitação do OpenID for Verifiable Presentations (OpenID4VP).
Adicionar dependências
No arquivo build.gradle do seu app, adicione as seguintes dependências do Gerenciador de credenciais:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02" }
Inicializar o Credential Manager
Use o contexto do app ou da atividade para criar um objeto CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Construir a solicitação de credencial digital
Para solicitar um e-mail verificado, construa um GetCredentialRequest
que contenha um GetDigitalCredentialOption. Essa opção exige uma string requestJson formatada como uma solicitação do OpenID for Verifiable Presentations (OpenID4VP).
O JSON de solicitação do OpenID4VP precisa seguir uma estrutura específica. Os provedores atuais
oferecem suporte a uma estrutura JSON com um wrapper externo "digital": {"requests":
[...]}.
val nonce = generateSecureRandomNonce()
// This request follows the OpenID4VP spec
val openId4vpRequest = """
{
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "$nonce",
"dcql_query": {
"credentials": [
{
"id": "user_info_query",
"format": "dc+sd-jwt",
"meta": {
"vct_values": ["UserInfoCredential"]
},
"claims": [
{"path": ["email"]},
{"path": ["name"]},
{"path": ["given_name"]},
{"path": ["family_name"]},
{"path": ["picture"]},
{"path": ["hd"]},
{"path": ["email_verified"]}
]
}
]
}
}
}
]
}
"""
val getDigitalCredentialOption = GetDigitalCredentialOption(requestJson = openId4vpRequest)
val request = GetCredentialRequest(listOf(getDigitalCredentialOption))
A solicitação contém as seguintes informações importantes:
Consulta DCQL: o
dcql_queryespecifica o tipo de credencial e as declarações solicitadas (email_verified). Você pode solicitar outras declarações para determinar o nível de verificação. Algumas declarações possíveis são as seguintes:email_verified: na resposta, é um booleano que indica se o e-mail foi verificado.hd(domínio hospedado): na resposta, está vazio.
nonce: um valor aleatório exclusivo e criptograficamente seguro é gerado para cada solicitação. Isso é fundamental para a segurança, já que impede ataques repetidos.
UserInfoCredential: esse valor implica um tipo específico de credencial digital que contém atributos do usuário. Incluir isso na solicitação é fundamental para distinguir o caso de uso de verificação de e-mail.
Em seguida, envolva o JSON openId4vpRequest em um GetDigitalCredentialOption, crie um GetCredentialRequest e chame getCredential().
Apresentar a solicitação ao usuário
Apresente a solicitação ao usuário usando a interface integrada do Credential Manager.
try {
// Requesting Digital Credential from user...
val result = credentialManager.getCredential(activity, request)
when (val credential = result.credential) {
is DigitalCredential -> {
val responseJsonString = credential.credentialJson
// Successfully received digital credential response.
// Next, parse this response and send it to your server.
// ...
}
else -> {
// handle Unexpected State() - Up to the developer
}
}
} catch (e: Exception) {
// handle exceptions - Up to the developer
}
Analisar a resposta no cliente
Depois de receber a resposta, você pode realizar uma análise preliminar no cliente. Isso é útil para atualizar imediatamente a interface, por exemplo, mostrando o nome do usuário.
O código a seguir extrai o JWT de divulgação seletiva (SD-JWT) bruto e usa um auxiliar para decodificar as declarações.
// 1. Parse the outer JSON wrapper to get the `vp_token`
val responseData = JSONObject(responseJsonString)
val dataObject = responseData.getJSONObject("data")
val vpToken = dataObject.getJSONObject("vp_token")
// 2. Extract the raw SD-JWT string
val credentialId = vpToken.keys().next()
val rawSdJwt = vpToken.getJSONArray(credentialId).getString(0)
// 3. Use your parser to get the verified claims
// Server-side validation/parsing is highly recommended.
// Assumes a local parser like the one in our SdJwtParser.kt sample
val claims = SdJwtParser.parse(rawSdJwt)
Log.d("TAG", "Parsed Claims: ${claims.toString(2)}")
// 4. Create your VerifiedUserInfo object with REAL data
val userInfo = VerifiedUserInfo(
email = claims.getString("email"),
displayName = claims.optString("name", claims.getString("email"))
)
Processar a resposta
A API Credential Manager vai retornar uma DigitalCredential
resposta.
Confira a seguir um exemplo de como é o responseJsonString bruto e como as declarações aparecem após a análise do SD-JWT interno, em que você também recebe metadados adicionais e o e-mail verificado:
/*
// Example of the raw JSON response from credential.credentialJson:
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"vp_token": {
// This key matches the 'id' you set in your dcql_query
"user_info_query": [
// The SD-JWT string (Issuer JWT ~ Disclosures ~ Key Binding JWT)
"eyJhbGciOiJ...~WyI...IiwgImVtYWlsIiwgInVzZXJAZXhhbXBsZS5jb20iXQ~...~eyJhbGciOiJ..."
]
}
}
}
// Example of the parsed and verified claims from the SD-JWT on your server:
{
"cnf": {
"jwk": {..}
},
"exp": 1775688222,
"iat": 1775083422,
"iss": "https://verifiablecredentials-pa.googleapis.com",
"vct": "UserInfoCredential",
"email": "jane.doe.246745@gmail.com",
"email_verified": true,
"given_name": "Jane",
"family_name": "Doe",
"name": "Jane Doe",
"picture": "http://example.com/janedoe/me.jpg",
"hd": ""
}
*/
Validação do lado do servidor para criação de contas
Como o e-mail recuperado é verificado criptograficamente, você pode omitir a etapa de verificação de OTP por e-mail, reduzindo significativamente o atrito de inscrição e possivelmente aumentando a conversão. É melhor processar isso no seu servidor. O cliente envia a resposta bruta (que contém o vp_token) e o nonce original para um novo endpoint do servidor.
Para verificação, seu aplicativo precisa enviar o responseJsonString completo ao servidor para validação criptográfica antes de criar uma conta ou fazer login do usuário.
A credencial digital oferece dois níveis críticos de verificação para o servidor:
- Autenticidade dos dados: a verificação do URL do emissor (
iss) e daSD-JWTassinatura prova que uma autoridade confiável emitiu esses dados. - Identidade do apresentador: a verificação do campo
cnfe da assinatura de vinculação de chave (kb) confirma que a credencial está sendo compartilhada pelo mesmo dispositivo em que foi emitida originalmente, impedindo que ela seja interceptada ou usada em outro dispositivo.
A validação no servidor precisa alcançar o seguinte:
- Verificar o emissor: verifique se o campo
iss(emissor) corresponde ahttps://verifiablecredentials-pa.googleapis.com. - Verificar a assinatura: verifique a assinatura do SD-JWT usando as chaves públicas (JWKs) disponíveis em https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.
Para segurança total, valide também o nonce para evitar ataques repetidos.
Ao combinar essas etapas, o servidor pode validar a autenticidade dos dados e a identidade do apresentador, garantindo que a credencial não tenha sido interceptada ou falsificada antes do provisionamento da nova conta.
try {
// Send the raw credential response and the original nonce to your server.
// Your server must validate the response. createAccountWithVerifiedCredentials
// is a custom implementation per each RP for server side verification and account creation.
val serverResponse = createAccountWithVerifiedCredentials(responseJsonString, nonce)
// Server returns the new account info (e.g., email, name)
val claims = JSONObject(serverResponse.json)
val userInfo = VerifiedUserInfo(
email = claims.getString("email"),
displayName = claims.optString("name", claims.getString("email"))
)
// handle response - Up to the developer
} catch (e: Exception) {
// handle exceptions - Up to the developer
}
Criação de chaves de acesso
Uma próxima etapa opcional, mas altamente recomendada, após o provisionamento de uma conta é criar imediatamente uma chave de acesso para ela. Isso oferece um método seguro e sem senha para o usuário fazer login. Esse fluxo é idêntico a um registro de chave de acesso padrão.
Suporte à WebView
Para que o fluxo funcione em uma WebView, os desenvolvedores precisam implementar uma
ponte JavaScript (JS Bridge) para facilitar a transferência. Essa ponte permite que o objeto WebView sinalize o app nativo, que pode fazer a chamada real para a API Credential Manager.