Umiejętności związane z Androidem
Wyświetl na GitHubPobieranie zweryfikowanego adresu e-mail
android skills add verified-emailPrzegląd
Z tego przewodnika dowiesz się, jak zaimplementować pobieranie zweryfikowanego adresu e-mail za pomocą interfejsu Digital Credentials Verifier API poprzez żądanie OpenID for Verifiable Presentations (OpenID4VP).
Dodawanie zależności
W pliku build.gradle aplikacji dodaj te zależności Credential Manager:
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" }
Inicjowanie Credential Manager
Użyj kontekstu aplikacji lub aktywności, aby utworzyć obiekt CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Tworzenie żądania Digital Credential
Aby poprosić o zweryfikowany adres e-mail, utwórz GetCredentialRequest
zawierający GetDigitalCredentialOption. Ta opcja wymaga ciągu requestJson sformatowanego jako żądanie OpenID for Verifiable Presentations (OpenID4VP).
Kod JSON żądania OpenID4VP musi mieć określoną strukturę. Obecni
dostawcy obsługują strukturę JSON z zewnętrznym "digital": {"requests":
[...]} opakowaniem.
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))
Żądanie zawiera te kluczowe informacje:
zapytanie DCQL:
dcql_queryokreśla typ danych logowania i żądane dane (email_verified). Możesz poprosić o inne dane, aby określić poziom weryfikacji. Oto kilka możliwych danych:email_verified: w odpowiedzi jest to wartość logiczna wskazująca, czy adres e-mail jest zweryfikowany.hd(domena hostowana): w odpowiedzi jest to puste pole.
nonce: dla każdego żądania generowana jest unikalna, bezpieczna kryptograficznie wartość losowa. Jest to bardzo ważne ze względów bezpieczeństwa, ponieważ zapobiega atakom typu replay.
UserInfoCredential: ta wartość oznacza określony typ danych logowania, które zawierają atrybuty użytkownika. Uwzględnienie tej wartości w żądaniu jest kluczowe, aby odróżnić przypadek użycia potwierdzania adresu e-mail.
Następnie opakuj kod JSON openId4vpRequest w GetDigitalCredentialOption, utwórz GetCredentialRequest i wywołaj getCredential().
Przedstawianie żądania użytkownikowi
Przedstaw użytkownikowi żądanie za pomocą wbudowanego interfejsu 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
}
Parsowanie odpowiedzi na kliencie
Po otrzymaniu odpowiedzi możesz wstępnie ją przeanalizować na kliencie. Jest to przydatne do natychmiastowego aktualizowania interfejsu, np. przez wyświetlanie nazwy użytkownika.
Poniższy kod wyodrębnia surowy token JWT z selektywnym ujawnianiem informacji (SD-JWT) i używa pomocnika do dekodowania jego danych.
// 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"))
)
Obsługa odpowiedzi
Interfejs Credential Manager API zwróci DigitalCredential
odpowiedź.
Poniżej znajdziesz przykład surowego ciągu responseJsonString oraz danych po przeanalizowaniu wewnętrznego tokena SD-JWT, w którym oprócz zweryfikowanego adresu e-mail uzyskasz też dodatkowe metadane:
/*
// 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": ""
}
*/
Weryfikacja po stronie serwera na potrzeby tworzenia konta
Ponieważ pobrany adres e-mail jest weryfikowany kryptograficznie, możesz pominąć krok weryfikacji kodu OTP, co znacznie zmniejszy trudności związane z rejestracją i potencjalnie zwiększy współczynnik konwersji. Ten proces najlepiej obsługiwać na serwerze. Klient wysyła surową odpowiedź (zawierającą vp_token) i oryginalną wartość nonce do nowego punktu końcowego serwera.
Aby przeprowadzić weryfikację, aplikacja musi wysłać pełny ciąg responseJsonString na serwer w celu weryfikacji kryptograficznej przed utworzeniem konta lub zalogowaniem użytkownika.
Dane logowania zapewniają 2 kluczowe poziomy weryfikacji na serwerze:
- Autentyczność danych: sprawdzenie adresu URL wystawcy (
iss) i podpisuSD-JWTpotwierdza, że dane zostały wydane przez zaufany organ. - Tożsamość prezentera: sprawdzenie pola
cnfi podpisu Key Binding (kb) potwierdza, że dane logowania są udostępniane przez to samo urządzenie, na którym zostały pierwotnie wydane, co uniemożliwia ich przechwycenie lub użycie na innym urządzeniu.
Weryfikacja na serwerze musi obejmować te czynności:
- Sprawdzenie wystawcy: upewnij się, że pole
iss(wystawca) jest zgodne zhttps://verifiablecredentials-pa.googleapis.com. - Sprawdzenie podpisu: sprawdź podpis SD-JWT za pomocą kluczy publicznych (JWK) dostępnych pod adresem https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.
Aby zapewnić pełne bezpieczeństwo, sprawdź też nonce, aby zapobiec atakom typu replay.
Dzięki połączeniu tych kroków serwer może sprawdzić zarówno autentyczność danych, jak i tożsamość prezentera, co gwarantuje, że dane logowania nie zostały przechwycone ani sfałszowane przed udostępnieniem nowego konta.
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
}
Tworzenie klucza dostępu
Opcjonalnym, ale wysoce zalecanym kolejnym krokiem po udostępnieniu konta jest natychmiastowe utworzenie klucza dostępu dla niego. Zapewnia to bezpieczną metodę logowania bez hasła. Ten proces jest identyczny ze standardową rejestracją klucza dostępu.
Obsługa WebView
Aby proces działał w WebView, programiści powinni zaimplementować
mostek JavaScript (JS Bridge), który ułatwi przekazywanie. Ten mostek umożliwia obiektowi WebView sygnalizowanie aplikacji natywnej, która może następnie wykonać rzeczywiste wywołanie interfejsu Credential Manager API.