يصف هذا الدليل كيفية تنفيذ عملية استرداد عناوين البريد الإلكتروني التي تم التحقّق منها باستخدام الـ Digital Credentials Verifier API من خلال طلب OpenID for Verifiable Presentations (OpenID4VP).
إضافة التبعيات
في ملف build.gradle الخاص بتطبيقك، أضِف التبعيات التالية لـ 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" }
إعداد Credential Manager
استخدِم سياق تطبيقك أو نشاطك لإنشاء عنصر CredentialManager.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
إنشاء طلب بيانات الاعتماد الرقمية
لطلب عنوان بريد إلكتروني تم التحقّق منه، أنشِئ GetCredentialRequest
يحتوي على GetDigitalCredentialOption. يتطلّب هذا الخيار سلسلة requestJson منسّقة كطلب OpenID for Verifiable Presentations (OpenID4VP).
يجب أن يتّبع JSON لطلب OpenID4VP بنية معيّنة. تتيح الجهات المقدّمة الحالية بنية JSON تتضمّن غلافًا خارجيًا "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))
يحتوي الطلب على المعلومات الأساسية التالية:
طلب بحث DCQL: يحدّد
dcql_queryنوع بيانات الاعتماد و المطالبات المطلوبة (email_verified). يمكنك طلب مطالبات أخرى لـ تحديد مستوى التحقّق. في ما يلي بعض المطالبات المحتمَلة:email_verified: في الردّ، تكون هذه القيمة منطقية تشير إلى ما إذا تم التحقّق من عنوان البريد الإلكتروني.hd(النطاق المستضاف): في الردّ، تكون هذه القيمة فارغة.
إذا كان عنوان البريد الإلكتروني لا ينتهي بـ @gmail.com، تحقّقت Google من هذا العنوان عند إنشاء حساب Google، ولكن لا توجد مطالبة بالجدة. لذلك، بالنسبة إلى عناوين البريد الإلكتروني غير التابعة لـ Google، عليك مراعاة تحدٍّ إضافي للتحقّق من هوية المستخدم، مثل كلمة المرور صالحة لمرة واحدة (OTP). لفهم مخطط بيانات الاعتماد و القواعد المحدّدة للتحقّق من صحة الحقول، مثل
email_verified، يُرجى الرجوع إلى أدلة Google Identity.nonce: يتم إنشاء قيمة عشوائية فريدة وآمنة مشفّرة لكل طلب. هذه القيمة ضرورية للأمان، لأنّها تمنع هجمات إعادة التشغيل.
UserInfoCredential: تشير هذه القيمة إلى نوع معيّن من بيانات الاعتماد الرقمية التي تحتوي على سمات المستخدم. إنّ تضمين هذه القيمة في الطلب أمر أساسي للتمييز بين حالة استخدام التحقّق من عنوان البريد الإلكتروني.
بعد ذلك، غلِّف JSON الخاص بـ openId4vpRequest في GetDigitalCredentialOption، وأنشِئ GetCredentialRequest، ثم استدعِ getCredential().
عرض الطلب على المستخدم
اعرض الطلب على المستخدم باستخدام واجهة المستخدم المضمّنة في 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
}
تحليل الردّ على العميل
بعد تلقّي الردّ، يمكنك إجراء تحليل أولي على العميل. يفيد ذلك في تعديل واجهة المستخدم على الفور، مثلاً من خلال عرض اسم المستخدم.
يستخرج الرمز التالي رمز JWT الخاص بالكشف الانتقائي (SD-JWT) الخام ويستخدم أداة مساعدة لفك ترميز المطالبات.
// 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"))
)
معالجة الردّ
ستعرض Credential Manager API ردًّا من النوع DigitalCredential.
في ما يلي مثال على شكل responseJsonString الخام وشكل المطالبات بعد تحليل SD-JWT الداخلي الذي تحصل فيه على بيانات وصفية إضافية بالإضافة إلى عنوان البريد الإلكتروني الذي تم التحقّق منه:
/*
// 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": ""
}
*/
التحقّق على صعيد الخادم لإنشاء الحساب
بما أنّه تم التحقّق من عنوان البريد الإلكتروني المسترد باستخدام التشفير، يمكنك حذف خطوة التحقّق من كلمة المرور لمرة واحدة المرسَلة إلى عنوان البريد الإلكتروني، ما يقلّل بشكل كبير من صعوبة عملية الاشتراك وقد يزيد من معدّل الإحالات الناجحة. من الأفضل معالجة هذه العملية على الخادم. يرسل العميل الردّ الخام (الذي يحتوي على vp_token) ورمز nonce الأصلي إلى نقطة نهاية خادم جديدة.
للتحقّق من الصحة، يجب أن يرسل تطبيقك responseJsonString الكامل إلى الخادم لإجراء عملية التحقّق من التشفير قبل إنشاء حساب أو تسجيل دخول المستخدم.
توفّر بيانات الاعتماد الرقمية مستويَين مهمّين من التحقّق من الصحة لخادمك:
- أصالة البيانات: يثبت التحقّق من عنوان URL للجهة المُصدرة (
iss) وتوقيعSD-JWTأنّ جهة موثوق بها أصدرت هذه البيانات. - هوية المُقدِّم: يؤكّد التحقّق من الحقل
cnfوتوقيع Key Binding (kb) أنّ بيانات الاعتماد تتم مشاركتها من الجهاز نفسه الذي تم إصدارها عليه في الأصل، ما يمنع اعتراضها أو استخدامها على جهاز آخر.
يجب أن يحقّق التحقّق من الصحة على الخادم ما يلي:
- التحقّق من الجهة المُصدرة: تأكَّد من أنّ الحقل
iss(الجهة المُصدرة) يطابقhttps://verifiablecredentials-pa.googleapis.com. - التحقّق من التوقيع: تحقَّق من توقيع SD-JWT باستخدام المفاتيح العامة (JWKs) المتاحة على https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.
لضمان الأمان الكامل، تأكَّد أيضًا من التحقّق من صحة nonce لمنع هجمات إعادة التشغيل.
من خلال الجمع بين هذه الخطوات، يمكن لخادمك التحقّق من أصالة البيانات وهوية المُقدِّم، ما يضمن عدم اعتراض بيانات الاعتماد أو تزييفها قبل توفير الحساب الجديد.
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
}
إنشاء مفتاح مرور
بعد توفير حساب، ننصحك بشدة باتّخاذ خطوة اختيارية ولكنها مهمة، وهي إنشاء مفتاح مرور لهذا الحساب على الفور. يوفر ذلك طريقة آمنة لتسجيل دخول المستخدم بدون كلمة مرور. يتطابق هذا المسار مع عملية تسجيل مفتاح مرور عادية.
دعم WebView
لكي يعمل المسار على WebView، على المطوّرين تنفيذ JavaScript bridge (JS Bridge) لتسهيل عملية النقل. يسمح هذا الجسر لـ WebView بإرسال إشارة إلى التطبيق الأصلي، الذي يمكنه بعد ذلك إجراء الاتصال الفعلي بـ Credential Manager API.