پیاده‌سازی درستی‌سنجی ایمیل با Digital Credentials API

نمای کلی

افزودن وابستگی‌ها

در فایل build.gradle برنامه‌تان، وابستگی‌های زیر را برای «مدیر اطلاعات کاربری» اضافه کنید:

کاتلین

dependencies {
    implementation("androidx.credentials:credentials:1.7.0-alpha03")
    implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha03")
}

شیار

dependencies {
    implementation "androidx.credentials:credentials:1.7.0-alpha03"
    implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha03"
}

مقداردهی اولیه 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 برای ارائه قابل‌تأیید» (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))

سپس، openId4vpRequest JSON را در 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 کامل را برای اعتبارسنجی رمزنگاری به سرورتان ارسال کند و سپس حساب ایجاد کند یا کاربر را به سیستم وارد کند.

  • اصالت داده‌ها: درستی‌سنجی نشانی وب صادرکننده (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 کار کند، توسعه‌دهندگان باید پل جاوا اسکریپت (JS Bridge) را برای تسهیل انتقال پیاده‌سازی کنند. این پل به شیء WebView اجازه می‌دهد به برنامه به زبان اصلی دستگاه سیگنال دهد، که سپس می‌تواند فراخوانی واقعی را به Credential Manager API انجام دهد.