אימות מספרי טלפון באמצעות אישורים דיגיטליים

במדריך הזה מוסבר איך להשתמש ב-DigitalCredential API כדי לקבל מספרי טלפון מאומתים של המשתמשים. התהליך כולל שני שלבים:

  1. שליחת בקשה ל-TS.43 token: אפליקציית הלקוח (המאמתת) שולחת בקשה לאסימון זמני של TS.43 מהמכשיר של המשתמש. ‫TS.43 token הוא פרט כניסה שהונפק על ידי הספק ומייצג את זהות המשתמש.
  2. החלפת הטוקן במספר טלפון: הקצה העורפי של האפליקציה מחליף את הטוקן TS.43 token עם צד שלישי או עם ספק סלולר כדי לקבל את מספר הטלפון המאומת של המשתמש.

תאימות לגרסת Android

ממשק ה-API לאימות מספר הטלפון נתמך ב-Android 10 (רמת API‏ 29) ובגרסאות מתקדמות יותר.

דרישות מוקדמות

כדי להטמיע אימות מספר טלפון באמצעות DigitalCredential API, צריך חשבון אצל ספק שירותי צד שלישי. מצטבר מבצע אינטראקציה עם ספקים ומספק את ממשק ה-API הנדרש לאפליקציה שלכם, בדרך כלל כנקודת קצה של API בענן שניתן לחיוב.

צריך גם להוסיף את יחסי התלות הבאים לסקריפט ה-build של Gradle:

Kotlin

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

Groovy

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

הטמעה

בדרך כלל התהליך המלא כולל את השלבים הבאים:

  1. בקשת פרמטרים של DCQL (שפת שאילתות של מסמכים דיגיטליים) מאגרגטור: מתקשרים לאגרגטור אחד או יותר ומבקשים קבוצה של פרמטרים של DCQL. בעזרת DCQL אפשר לציין את פרטי הכניסה הדיגיטליים המדויקים שאתם צריכים מכל צובר.
  2. יוצרים את בקשת OpenID4VP: בבקשות OpenID4VP שיוצרים בבק-אנד של האפליקציה, כוללים את הפרמטרים של DCQL מהאגרגטור. לאחר מכן, שולחים את בקשת OpenID4VP לאפליקציית הלקוח.

  3. הפעלת Credential Manager API: באפליקציית הלקוח, משתמשים ב-Credential Manager API כדי לשלוח את בקשת OpenID4VP למערכת ההפעלה. בתגובה, מקבלים אובייקט תגובה של OpenID4VP שמכיל את TS.43 Digital Credential. פרטי הכניסה האלה מוצפנים, ורק המצטבר המשויך יכול לפענח אותם. אחרי שמקבלים את האסימון של חברת התובלה, שולחים את התגובה מאפליקציית הלקוח אל ה-Backend של האפליקציה.

  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: ערך ייחודי שנוצר על ידי ה-Backend לכל בקשה. ה-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

באפליקציית הלקוח, מבצעים קריאה ל-Credential Manager API, עם בקשת DigitalCredential 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 בחזרה לשרת הבק-אנד, שם אפשר לאמת אותה ולהשתמש בה כדי להחליף אותה במספר הטלפון המאומת באמצעות צד שלישי.

אימות התשובה של פרטי הכניסה הדיגיטליים

הדוגמה הבאה מראה איך לנתח את התגובה ולבצע את שלב האימות בבק-אנד של האפליקציה:

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)