Hướng dẫn này mô tả cách triển khai quy trình truy xuất email đã xác minh bằng Digital Credentials Verifier API thông qua yêu cầu OpenID for Verifiable Presentations (OpenID4VP).
Thêm phần phụ thuộc
Trong tệp build.gradle của ứng dụng, hãy thêm các phần phụ thuộc sau cho Trình quản lý thông tin xác thực:
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" }
Khởi chạy Trình quản lý thông tin xác thực
Sử dụng ngữ cảnh ứng dụng hoặc hoạt động để tạo đối tượng CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
Tạo yêu cầu về Thông tin xác thực kỹ thuật số
Để yêu cầu một email đã xác minh, hãy tạo một GetCredentialRequest chứa GetDigitalCredentialOption. Tuỳ chọn này yêu cầu một chuỗi requestJson được định dạng dưới dạng yêu cầu OpenID cho Bản trình bày có thể xác minh (OpenID4VP).
JSON yêu cầu OpenID4VP phải tuân theo một cấu trúc cụ thể. Các nhà cung cấp hiện tại hỗ trợ cấu trúc JSON có trình bao bọc "digital": {"requests":
[...]} bên ngoài.
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))
Yêu cầu này chứa những thông tin chính sau:
Truy vấn DCQL:
dcql_querychỉ định loại thông tin đăng nhập và các yêu cầu đang được yêu cầu (email_verified). Bạn có thể yêu cầu các yêu cầu khác để xác định mức độ xác minh. Sau đây là một số tuyên bố có thể:email_verified: Trong phản hồi, đây là một giá trị Boolean cho biết email có được xác minh hay không.hd(tên miền được lưu trữ): Trong phản hồi, trường này trống.
Nếu email không phải là email có đuôi @gmail.com, thì Google đã xác minh email này khi Tài khoản Google được tạo, nhưng không có thông tin nào về độ mới. Do đó, đối với email không phải của Google, bạn nên cân nhắc một thử thách bổ sung, chẳng hạn như OTP, để xác minh người dùng. Để hiểu giản đồ của thông tin đăng nhập và các quy tắc cụ thể để xác thực các trường như
email_verified, hãy tham khảo hướng dẫn về danh tính của Google.nonce: Một giá trị ngẫu nhiên, duy nhất và được bảo mật bằng mật mã sẽ được tạo cho mỗi yêu cầu. Điều này rất quan trọng đối với tính bảo mật vì nó ngăn chặn các cuộc tấn công phát lại.
UserInfoCredential: Giá trị này ngụ ý một loại thông tin đăng nhập kỹ thuật số cụ thể có chứa các thuộc tính của người dùng. Việc đưa thông tin này vào yêu cầu là yếu tố then chốt để phân biệt trường hợp sử dụng xác minh qua email.
Tiếp theo, hãy gói JSON openId4vpRequest trong GetDigitalCredentialOption, tạo GetCredentialRequest và gọi getCredential().
Đưa ra yêu cầu cho người dùng
Cho người dùng xem yêu cầu bằng giao diện người dùng tích hợp sẵn của Trình quản lý thông tin xác thực.
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
}
Phân tích cú pháp phản hồi trên ứng dụng
Sau khi nhận được phản hồi, bạn có thể phân tích sơ bộ trên máy khách. Điều này hữu ích cho việc cập nhật ngay giao diện người dùng, chẳng hạn như bằng cách cho thấy tên của người dùng.
Đoạn mã sau đây trích xuất JWT công bố có chọn lọc (SD-JWT) thô và sử dụng một trình trợ giúp để giải mã các khai báo của JWT này.
// 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"))
)
Xử lý phản hồi
Credential Manager API sẽ trả về một phản hồi DigitalCredential.
Sau đây là ví dụ về responseJsonString thô và các xác nhận quyền sở hữu sau khi phân tích cú pháp SD-JWT bên trong, trong đó bạn cũng nhận được siêu dữ liệu bổ sung cùng với email đã xác minh:
/*
// 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": ""
}
*/
Xác thực phía máy chủ để tạo tài khoản
Vì email đã truy xuất được xác minh bằng mật mã, nên bạn có thể bỏ qua bước xác minh OTP qua email, giảm đáng kể sự phiền toái khi đăng ký và có khả năng tăng lượt chuyển đổi. Quá trình này nên được xử lý trên máy chủ của bạn. Ứng dụng gửi phản hồi thô (chứa vp_token) và số chỉ dùng một lần ban đầu đến một điểm cuối máy chủ mới.
Để xác minh, ứng dụng của bạn phải gửi toàn bộ responseJsonString đến máy chủ của bạn để xác thực bằng mật mã trước khi tạo tài khoản hoặc đăng nhập cho người dùng.
Thông tin đăng nhập kỹ thuật số cung cấp 2 cấp độ xác minh quan trọng cho máy chủ của bạn:
- Tính xác thực của dữ liệu: Việc xác minh URL của tổ chức phát hành (
iss) và chữ kýSD-JWTchứng minh rằng một cơ quan đáng tin cậy đã phát hành dữ liệu này. - Danh tính của người trình bày: Việc xác minh trường
cnfvà chữ ký Liên kết khoá (kb) xác nhận rằng thông tin xác thực đang được chia sẻ bởi chính thiết bị đã phát hành thông tin xác thực đó ban đầu, ngăn chặn thông tin xác thực bị chặn hoặc sử dụng trên một thiết bị khác.
Quy trình xác thực trên máy chủ phải đạt được những điều sau:
- Xác minh tổ chức phát hành: Đảm bảo trường
iss(tổ chức phát hành) khớp vớihttps://verifiablecredentials-pa.googleapis.com. - Xác minh chữ ký: Kiểm tra chữ ký của SD-JWT bằng các khoá công khai (JWK) có tại https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.
Để đảm bảo an toàn tuyệt đối, hãy nhớ xác thực nonce để ngăn chặn các cuộc tấn công phát lại.
Bằng cách kết hợp các bước này, máy chủ của bạn có thể xác thực cả tính xác thực của dữ liệu và danh tính của người trình bày, đảm bảo rằng thông tin đăng nhập không bị chặn hoặc giả mạo trước khi cung cấp tài khoản mới.
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
}
Tạo khoá truy cập
Bước tiếp theo không bắt buộc nhưng bạn nên thực hiện ngay sau khi cấp tài khoản là tạo khoá truy cập cho tài khoản đó. Điều này mang đến cho người dùng một phương thức đăng nhập an toàn và không cần mật khẩu. Quy trình này giống hệt với quy trình đăng ký khoá truy cập tiêu chuẩn.
Hỗ trợ WebView
Để quy trình này hoạt động trên WebView, nhà phát triển nên triển khai cầu nối JavaScript (JS Bridge) để hỗ trợ việc chuyển giao. Cầu nối này cho phép WebView báo hiệu cho ứng dụng gốc, sau đó ứng dụng này có thể thực hiện lệnh gọi thực tế đến Credential Manager API.