پیاده‌سازی درستی‌سنجی شماره تلفن

این راهنما نحوه استفاده از DigitalCredential API را برای دریافت شماره تلفن‌های درستی‌سنجی‌شده برای کاربران شما شرح می‌دهد. این فرایند شامل دو مرحله است:

  1. پیش‌بررسی: با پُرسمان از Android Telephony API، بررسی کنید که آیا شرکت مخابراتی کاربر از این API پشتیبانی می‌کند یا نه. اگر شرکت مخابراتی پشتیبانی نمی‌شود، به جریان پیامک عادی‌تان برگردید.
  2. درخواست TS.43 token: برنامه کارخواه شما («تأییدکننده») درخواست یک کلید TS.43 موقت از دستگاه کاربر می‌کند. TS.43 token یک اعتبارنامه صادرشده ازسوی شرکت مخابراتی است که هویت کاربر را نشان می‌دهد.
  3. مبادله کردن کد با شماره تلفن: پسای برنامه شما TS.43 token را با یک تجمیع‌کننده یا شرکت مخابراتی برای شماره تلفن درستی‌سنجی‌شده کاربر مبادله می‌کند.

سازگاری نسخه Android

Phone Number Verification API در Android نسخه ۱۰ (سطح API‏ ۲۹) و بالاتر پشتیبانی می‌شود.

پیش‌نیازها

برای پیاده‌سازی درستی‌سنجی شماره تلفن با DigitalCredential API، به حسابی با تجمیع‌کننده یا اپراتور تلفن همراه نیاز دارید. یک تجمیع‌کننده با شرکت‌های مخابراتی تعامل دارد و سطح API لازم را برای برنامه شما فراهم می‌کند، معمولاً به‌عنوان یک نقطه پایانی API ابری قابل پرداخت. در این راهنما، هم تجمیع‌کنندگان و هم شرکت‌های مخابراتی به‌طور جمعی تجمیع‌کنندگان نامیده می‌شوند.

همچنین باید وابستگی‌های زیر را به اسکریپت ساخت 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"
}

پیاده‌سازی

قبل‌از فراخوانی Digital Credentials API، بااستفاده از Android Telephony API بررسی کنید که آیا شرکت مخابراتی کاربر از این ویژگی پشتیبانی می‌کند یا نه.

اجازه READ_PHONE_STATE را به فایل AndroidManifest.xml اضافه کنید.

val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val carrierId = telephonyManager.getSimCarrierId()

با این شناسه، پشتیبانی TS.43 را با پس‌بست تجمیع‌کننده خود درستی‌سنجی کنید. اگر شرکت مخابراتی این کاربر پشتیبانی نمی‌شود، می‌توانید به روش جایگزینی مثل پیامک برگردید، زیرا فراخوانی Digital Credentials API نتیجه‌ای خالی برمی‌گرداند. درغیراین‌صورت، با جریان «اعتبارنامه‌های دیجیتال» ادامه دهید.

فرایند سرتاسری معمولاً این مراحل را دنبال می‌کند:

  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 را به تجمیع‌کننده ارسال کنید. تجمیع‌کننده اعتبارنامه را اعتبارسنجی می‌کند و شماره تلفن درستی‌سنجی‌شده را برمی‌گرداند.

تصویری که جریان درخواست درستی‌سنجی شماره تلفن را نشان می‌دهد
شکل ۱.چرخه عمر درخواست درستی‌سنجی شماره تلفن، که با درخواست پارامترها ازسوی پسای «درستی‌سنج» از تجمیع‌کننده شروع می‌شود و با برگرداندن شماره تلفن درستی‌سنجی‌شده پایان می‌یابد.

درخواست پارامترهای 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: مقدار یکتایی که با پسین شما برای هر درخواست تولید می‌شود. nonce در شیء اعتبارنامه DCQL تجمیع‌کننده باید با این nonce مطابقت داشته باشد.
  • dcql_query: در این مورد، از dcql_query برای مشخص کردن اینکه TS.43 Digital Credential درخواست شده است استفاده کنید. همچنین می‌توانید سایر اطلاعات کاربری دیجیتال را در اینجا درخواست کنید.

سپس، درخواست OpenID4VP را در شیء درخواست DigitalCredential API قرار دهید و آن را به برنامه کارخواه ارسال کنید.

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

در برنامه کارخواه، با درخواست DigitalCredential API که توسط زیرینه برنامه‌تان ارائه شده است، با Credential Manager API تماس بگیرید.

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)