تنفيذ عملية إثبات ملكية عنوان البريد الإلكتروني باستخدام Digital Credentials API

نظرة عامة

إضافة التبعيات

في ملف 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))

بعد ذلك، عليك تضمين ملف JSON الخاص بـ openId4vpRequest في GetDigitalCredentialOption، وإنشاء GetCredentialRequest، واستدعاء getCredential().

عرض الطلب على المستخدم

اعرض الطلب على المستخدم باستخدام واجهة المستخدم المضمّنة في Credential Manager.

coroutineScope {
    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 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"))
)

معالجة الردّ

ستعرض واجهة برمجة التطبيقات Credential Manager API الاستجابة DigitalCredential.

في ما يلي مثال على شكل responseJsonString الأوّلي، وشكل المطالبات بعد تحليل SD-JWT الداخلي حيث تحصل على بيانات وصفية إضافية مع البريد الإلكتروني الذي تم التحقّق منه:

/*
// 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": ""
}
 */

التحقّق على صعيد الخادم لإنشاء الحسابات

لإثبات الهوية، يجب أن يرسل تطبيقك رمز responseJsonString الكامل إلى الخادم لإجراء عملية التحقّق من التشفير قبل إنشاء حساب أو تسجيل دخول المستخدم.

  • صحة البيانات: يثبت التحقّق من عنوان URL الخاص بجهة الإصدار (iss) وتوقيع SD-JWT أنّ جهة موثوقة أصدرت هذه البيانات.

يجب أن يحقّق التحقّق من الصحة على الخادم ما يلي:

  • التحقّق من جهة الإصدار: تأكَّد من أنّ الحقل iss (جهة الإصدار) يتطابق مع https://verifiablecredentials-pa.googleapis.com.
  • التحقّق من التوقيع: تحقَّق من توقيع SD-JWT باستخدام المفاتيح العامة (JWK) المتوفّرة على الرابط 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، على المطوّرين تنفيذ JavaScript bridge (JS Bridge) لتسهيل عملية النقل. يتيح هذا الجسر لكائن WebView إرسال إشارة إلى التطبيق الأصلي، والذي يمكنه بعد ذلك إجراء الطلب الفعلي إلى Credential Manager API.