تأكيد أرقام الهواتف باستخدام مستندات التعريف الرقمية

يوضّح هذا الدليل بالتفصيل كيفية استخدام DigitalCredential API للحصول على أرقام هواتف تم تأكيدها لمستخدميك. تتضمّن العملية خطوتَين:

  1. طلب TS.43 token: يطلب تطبيق العميل (الذي يُعرف باسم "المُثبِت") رمزًا مؤقتًا من نوع TS.43 من جهاز المستخدم. TS.43 token هو بيانات اعتماد صادرة عن مشغّل شبكة الجوّال وتمثّل هوية المستخدم.
  2. استبدال الرمز برقم هاتف: يستبدل خادم الخلفية في تطبيقك TS.43 token مع مجمّع أو مشغّل شبكة الجوّال للحصول على رقم هاتف المستخدم الذي تم تأكيده.

التوافق مع إصدارات Android

تتوافق واجهة برمجة التطبيقات لإثبات رقم الهاتف مع Android 10 (مستوى واجهة برمجة التطبيقات 29) والإصدارات الأحدث.

المتطلبات الأساسية

لتنفيذ إثبات رقم الهاتف باستخدام DigitalCredential API، تحتاج إلى حساب لدى مجمّع. يتفاعل المجمّع مع مشغّلي شبكات الجوّال ويوفر واجهة برمجة التطبيقات اللازمة لتطبيقك، وعادةً ما تكون نقطة نهاية لواجهة برمجة تطبيقات سحابية قابلة للفوترة.

عليك أيضًا إضافة التبعيات التالية إلى نص Gradle البرمجي للإصدار:

Kotlin

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

Groovy

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

التنفيذ

تتّبع العملية الشاملة الخطوات التالية بشكل عام:

  1. **طلب مَعلمات DCQL (لغة طلب بيانات الاعتماد الرقمية) من مجمّع**: يمكنك استدعاء مجمّع واحد أو أكثر وطلب مجموعة من مَعلمات DCQL. تتيح لك لغة DCQL تحديد بيانات الاعتماد الرقمية الدقيقة التي تحتاج إليها من كل مجمّع.
  2. إنشاء طلب OpenID4VP: من خادم الخلفية في تطبيقك، يمكنك إنشاء طلب OpenID4VP، مع تضمين مَعلمات DCQL من المجمّع. بعد ذلك، أرسِل طلب OpenID4VP إلى تطبيق العميل.

  3. استدعاء Credential Manager API: في تطبيق العميل، استخدِم Credential Manager API لإرسال طلب OpenID4VP إلى نظام التشغيل. في المقابل، ستتلقّى عنصر استجابة OpenID4VP يحتوي على TS.43 Digital Credential. هذه البيانات مشفّرة ولا يمكن فك تشفيرها إلا من خلال المجمّع المرتبط بها. بعد تلقّي رمز مشغّل شبكة الجوّال، أرسِل الردّ من تطبيق العميل إلى خادم الخلفية في التطبيق.

  4. **التحقّق من صحة الردّ**: في خادم الخلفية في تطبيقك، تحقَّق من صحة استجابة OpenID4VP.

  5. استبدال الرمز برقم هاتف: من خادم الخلفية في تطبيقك، أرسِل TS.43 Digital Credential إلى المجمّع. يتحقّق المجمّع من صحة بيانات الاعتماد ويُرجع رقم الهاتف الذي تم تأكيده.

صورة توضّح تدفّق طلب إثبات رقم الهاتف
الشكل 1:دورة طلب تأكيد رقم الهاتف، بدءًا من خادم الخلفية في "المُثبِت" الذي يطلب مَعلمات من مجمّع وانتهاءً برقم هاتف تم تأكيده.

طلب مَعلمات DCQL من مجمّع

من خادم الخلفية في تطبيقك، أرسِل طلبًا إلى المجمّع للحصول على عنصر بيانات اعتماد بلغة طلب بيانات الاعتماد الرقمية (DCQL). احرص على تقديم رقم خاص ورقم تعريف للطلب في طلبك. يُرجع المجمّع عنصر بيانات اعتماد DCQL، الذي يتضمّن بنية مشابهة لما يلي:

{
  // The credential ID is mapped to the request ID that is sent in your request to the aggregator.
  "id": "aggregator1",
  "format": "dc-authorization+sd-jwt",
  "meta": {
    "vct_values": [
      "number-verification/device-phone-number/ts43"
    ],
    "credential_authorization_jwt": "..."
  },
  "claims": [
    {
      "path": ["subscription_hint"],
      "values": [1]
    },
    {
      "path": ["phone_number_hint"],
      "values": ["+14155552671"]
    }
  ]
}

إنشاء طلب OpenID4VP

أولاً، من خادم الخلفية في تطبيقك، أنشئ عنصر dcql_query من خلال وضع عنصر بيانات اعتماد DCQL في مصفوفة credentials مدمجة ضمن عنصر dcql_query كما هو موضّح في المثال التالي:

"dcql_query": {
  "credentials": [
      "id": "aggregator1",
      "format": "dc-authorization+sd-jwt",
      "meta": {
        "vct_values": [
          "number-verification/device-phone-number/ts43"
        ],
        "credential_authorization_jwt": "..."
      },
      "claims": [
        {
          "path": ["subscription_hint"],
          "values": [1]
        },
        {
          "path": ["phone_number_hint"],
          "values": ["+14155552671"]
        }
      ]
  ]
}

بعد ذلك، أنشئ طلب OpenID4VP بالبنية التالية:

{
  "protocol": "openid4vp-v1-unsigned",
  "data": {
    "response_type": "vp_token",
    "response_mode": "dc_api",
    "nonce": "...",
    "dcql_query": { ... }
  }
}
  • protocol: يجب ضبط هذه المَعلمة على openid4vp-v1-unsigned لطلبات تأكيد رقم الهاتف.
  • response_type وresponse_mode: ثوابت تشير إلى تنسيق الطلب بقيمتَين ثابتتَين هما vp_token وdc_api على التوالي.
  • nonce: قيمة فريدة ينشئها خادم الخلفية لكل طلب. يجب أن تتطابق هذه القيمة مع القيمة العشوائية في عنصر بيانات اعتماد DCQL الخاص بالمجمّع.
  • dcql_query: في هذه الحالة، استخدِم dcql_query لتحديد أنّه يتم طلب TS.43 Digital Credential. يمكنك أيضًا طلب بيانات اعتماد رقمية أخرى هنا.

بعد ذلك، غلِّف طلب OpenID4VP في عنصر طلب بيانات من واجهة برمجة التطبيقات DigitalCredential وأرسِله إلى تطبيق العميل.

{
  "requests":
    [
      {
        "protocol": "openid4vp-v1-unsigned",
        "data": {
          "response_type": "vp_token",
          "response_mode": "dc_api",
          "nonce": "...",
          "dcql_query": { ... }
        }
      }
    ]
}

يوضّح المقتطف التالي كيفية إنشاء طلب DigitalCredential API:

def GenerateDCRequest():
    credentials = []
    aggregator1_dcql = call_aggregator_endpoint(nonce, "aggregator1", additional_params)
    credentials.append(aggregator1_dcql) # You can optionally work with multiple
    # aggregators, or request other types of credentials

    val dc_request =
    {
      "requests":
        [
          {
            "protocol": "openid4vp-v1-unsigned",
            "data": {
              "response_type": "vp_token",
              "response_mode": "dc_api",
              "nonce": "...",
              "dcql_query": {"credentials": credentials}
            }
          }
        ]
    }
    return dc_request

استدعاء Credential Manager API

في تطبيق العميل، استدعِ Credential Manager API باستخدام طلب بيانات من واجهة برمجة التطبيقات DigitalCredential الذي يقدّمه خادم الخلفية في تطبيقك.

val requestJson = generateTs43DigitalCredentialRequestFromServer()
val digiCredOption = GetDigitalCredentialOption(requestJson = requestJson)
val getCredRequest = GetCredentialRequest(
    listOf(digiCredOption)
)

coroutineScope.launch {
  try {
    val response = credentialManager.getCredential(
      context = activityContext,
      request = getCredRequest
    )
    val credential = response.credential
    when (credential) {
      is DigitalCredential -> {
        val responseJson = credential.credentialJson
        validateResponseOnServer(responseJson)
      }
      else -> {
        // Catch any unrecognized credential type here.
        Log.e(TAG, "Unexpected type of credential ${credential.type}")
      }
    }
  } catch (e : GetCredentialException) {
      // If user cancels the operation, the feature isn't available, or the
      // SIM doesn't support the feature, a GetCredentialCancellationException
      // will be returned. Otherwise, a GetCredentialUnsupportedException will
      // be returned with details in the exception message.
      handleFailure(e)
  }
}

يحتوي الردّ من DigitalCredential API على استجابة OpenID4VP. في ما يلي مثال على بيانات اعتماد JSON نموذجية من نتيجة DigitalCredential:

{
  "protocol": "openid4vp-v1-unsigned",

  "data": {
    "vp_token": {
      "aggregator1": ["eyJhbGciOiAiRVMy..."] # The encrypted TS.43 Digital
                                             # Credential in an array structure.
    }
  }
}

من تطبيق العميل، أرسِل الردّ من DigitalCredential API مرة أخرى إلى خادم الخلفية حيث يمكن التحقّق من صحته واستخدامه لاستبداله برقم الهاتف الذي تم تأكيده مع مجمّع.

في بعض الحالات، قد يحتوي الردّ على خطأ TS.43. استجابة الخطأ هي عنصر JSON يتّبع تنسيق استجابة الخطأ في OpenID4VP:

{
  "protocol": "openid4vp-v1-unsigned",

  "data": {
    "error": "<error_code>",
    "error_description": "<Human-readable description of the error>",
  }
}

هناك قيمتان محتملتان لـ error_code:

  • invalid_request: يشير هذا الرمز إلى أنّ الطلب غير صالح.
  • server_error: يشير هذا الرمز إلى حدوث حالات فشل أثناء معالجة الطلب. قد تكون هذه الأخطاء محلية أو مشاكل في TS.43.

يوفّر الحقل error_description تفاصيل إضافية حول المشكلة.

التحقّق من صحة الردّ من "بيانات الاعتماد الرقمية"

في ما يلي مثال على كيفية تحليل الردّ وتنفيذ خطوة التحقّق من صحته في خادم الخلفية في تطبيقك:

def processDigitalCredentialsResponse(response):
  # Step 1: Parse out the TS.43 Digital Credential from the response
  openId4VpResponse = response['data']

  ts43_digital_credential = response['vp_token']["aggregator1"][0]

  # Step 2: Perform response validation
  verifyResponse(ts43_digital_credential)

def verifyResponse(ts43_digital_credential):
  # The returned ts43_digital_credential is an SD-JWT-based Verifiable Credentials
  # (SD-JWT VC) as defined in this IETF spec. The section 3.4 of the specification
  # outlines how to validate the credential. At a high level, the steps involves
  # validating (1) the nonce in the response credential matches the one in the
  # request, (2) the integrity of the credential by checking the credential is
  # signed by the trusted issuer Android Telephony, and (3) other validity
  # properties associated with this credential, such as issue time and expiration
  # time

  # In most cases, you can use an SD-JWT VC library to perform these validations.

  # Some aggregators may also perform the validation logic for you. Check with your
  # aggregator to decide the exact scope of the validation required.

استبدال الرمز برقم هاتف

من خادم الخلفية في تطبيقك، أرسِل TS.43 Digital Credential الذي تم التحقّق من صحته إلى نقطة نهاية المجمّع، للتحقّق من صحة بيانات الاعتماد وتلقّي رقم الهاتف الذي تم تأكيده.

def processDigitalCredentialsResponse(response):
  # ... prior steps

  # Step 3: Call aggregator endpoint to exchange the verified phone number
  callAggregatorPnvEndpoint(ts43_digital_credential)