Z tego przewodnika dowiesz się, jak wdrożyć pobieranie zweryfikowanego adresu e-mail za pomocą interfejsu Digital Credentials Verifier API przy użyciu żądania OpenID for Verifiable Presentations (OpenID4VP).
Dodawanie zależności
W pliku build.gradle aplikacji dodaj te zależności dla Menedżera danych logowania:
dependencies {
implementation("androidx.credentials:credentials:1.7.0-alpha02")
implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02")
}
Inicjowanie Menedżera danych logowania
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 cyfrowego dokumentu tożsamości
Aby poprosić o e-mail weryfikacyjny, utwórz obiekt GetCredentialRequest zawierający GetDigitalCredentialOption. Ta opcja wymaga ciągu znaków requestJson sformatowanego jako żądanie OpenID for Verifiable Presentations (OpenID4VP).
Plik JSON żądania OpenID4VP musi mieć określoną strukturę. Obecni dostawcy obsługują strukturę JSON z zewnętrznym kodem „digital": {"requests”: [...]} wrapper.
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 uwierzytelniających i żądane uprawnienia (email_verified). Możesz poprosić o inne uprawnienia, aby określić poziom weryfikacji. Oto kilka możliwych twierdzeń:email_verified: w odpowiedzi jest to wartość logiczna, która wskazuje, czy adres e-mail został zweryfikowany.hd(domena hostowana): w odpowiedzi to pole jest puste.
- nonce: dla każdego żądania generowana jest unikalna, kryptograficznie bezpieczna wartość losowa. Ma to kluczowe znaczenie dla bezpieczeństwa, ponieważ zapobiega atakom typu replay.
UserInfoCredential: ta wartość oznacza określony typ cyfrowego dokumentu tożsamości, który zawiera atrybuty użytkownika. Uwzględnienie tego w żądaniu jest kluczowe, aby odróżnić przypadek użycia potwierdzania adresu e-mail.
Następnie umieść obiekt JSON openId4vpRequest w obiekcie GetDigitalCredentialOption, utwórz obiekt GetCredentialRequest i wywołaj metodę getCredential().
Przedstawienie prośby użytkownikowi
Wyświetl użytkownikowi prośbę, korzystając z wbudowanego interfejsu Menedżera danych logowania.
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
}
Analizowanie odpowiedzi na kliencie
Po otrzymaniu odpowiedzi możesz przeprowadzić wstępną analizę po stronie klienta. Jest to przydatne, gdy chcesz natychmiast zaktualizować interfejs, np. wyświetlić nazwę użytkownika.
Poniższy kod wyodrębnia nieprzetworzony token JWT z ujawnianiem selektywnym (SD-JWT) i używa funkcji pomocniczej do dekodowania jego deklaracji.
// 1. Parse the outer JSON wrapper to get the `vp_token`
val responseData = JSONObject(responseJsonString)
val vpToken = responseData.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 odpowiedź DigitalCredential.
Poniżej znajdziesz przykład nieprzetworzonego tokena responseJsonString oraz uprawnień po przeanalizowaniu wewnętrznego tokena SD-JWT, w którym oprócz zweryfikowanego adresu e-mail otrzymujesz dodatkowe metadane:
/*
// Example of the raw JSON response from credential.credentialJson:
{
"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 podczas tworzenia konta
Pobrany adres e-mail jest weryfikowany kryptograficznie, więc możesz pominąć krok weryfikacji adresu e-mail za pomocą hasła jednorazowego, co znacznie ułatwia rejestrację i może zwiększyć liczbę konwersji. Najlepiej przeprowadzić ten proces na serwerze. Klient wysyła nieprzetworzoną odpowiedź (zawierającą vp_token) i pierwotną liczbę jednorazową do nowego punktu końcowego serwera.
W celu weryfikacji aplikacja musi wysłać pełny obiekt responseJsonString na serwer, aby przeprowadzić weryfikację kryptograficzną, zanim utworzy konto lub zaloguje użytkownika.
Cyfrowe dane uwierzytelniające zapewniają 2 ważne poziomy weryfikacji serwera:
- Autentyczność danych: weryfikacja adresu URL wydawcy (
iss) i podpisuSD-JWTpotwierdza, że te dane zostały wydane przez zaufany organ. - Tożsamość osoby okazującej dokument: weryfikacja pola
cnfi podpisu powiązania klucza (kb) potwierdza, że dokument jest udostępniany przez to samo urządzenie, na które został pierwotnie wydany, co zapobiega jego przechwyceniu lub użyciu na innym urządzeniu.
Weryfikacja na serwerze musi spełniać te wymagania:
- Zweryfikuj wydawcę: upewnij się, że pole
iss(wydawca) jest zgodne zhttps://verifiablecredentials-pa.googleapis.com. - Weryfikacja podpisu: sprawdź podpis SD-JWT za pomocą kluczy publicznych (JWK) dostępnych na stronie 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 zweryfikować zarówno autentyczność danych, jak i tożsamość osoby, która je przedstawia, co daje pewność, że dokument nie został przechwycony ani sfałszowany 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 zdecydowanie zalecanym kolejnym krokiem po udostępnieniu konta jest natychmiastowe utworzenie klucza dostępu do tego konta. Zapewnia to użytkownikowi bezpieczną metodę logowania bez hasła. Ten proces jest identyczny ze standardową rejestracją klucza dostępu.
Obsługa WebView
Aby proces działał na urządzeniu WebView, deweloperzy powinni wdrożyć mostek JavaScript (JS Bridge), który ułatwi przekazywanie danych. Ten most umożliwia obiektowi WebView wysyłanie sygnałów do aplikacji natywnej, która może następnie wykonać rzeczywiste wywołanie interfejsu Credential Manager API.