Valider des numéros de téléphone avec des certifications numériques

Ce guide explique comment utiliser l'API DigitalCredential pour obtenir les numéros de téléphone validés de vos utilisateurs. Le processus comporte deux étapes :

  1. Demander un TS.43 token : votre application cliente (le "validateur") demande un jeton TS.43 temporaire à l'appareil de l'utilisateur. Le TS.43 token est un identifiant émis par l'opérateur qui représente l'identité de l'utilisateur.
  2. Échanger le jeton contre un numéro de téléphone : le backend de votre application échange le TS.43 token avec un agrégateur ou un opérateur pour obtenir le numéro de téléphone validé de l'utilisateur.

Compatibilité avec les versions d'Android

L'API de validation du numéro de téléphone est compatible avec Android 10 (niveau d'API 29) et versions ultérieures.

Prérequis

Pour implémenter la validation du numéro de téléphone avec l'API DigitalCredential, vous devez disposer d'un compte auprès d'un agrégateur. Un agrégateur interagit avec les opérateurs et fournit l'interface d'API nécessaire à votre application, généralement sous la forme d'un point de terminaison d'API cloud facturable.

Vous devez également ajouter les dépendances suivantes à votre script de compilation 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"
}

Implémentation

Le processus de bout en bout suit généralement les étapes suivantes :

  1. Demander des paramètres DCQL (Digital Credential Query Language) à un agrégateur : appelez un ou plusieurs agrégateurs et demandez un ensemble de paramètres DCQL parameters. DCQL vous permet de spécifier les identifiants numériques exacts dont vous avez besoin auprès de chaque agrégateur.
  2. Créer la requête OpenID4VP : à partir du backend de votre application, créez la requête OpenID4VP, tout en incluant les paramètres DCQL de l'agrégateur. Envoyez ensuite la requête OpenID4VP à votre application cliente.

  3. : dans votre application cliente, utilisez l' API Credential Manager pour envoyer la requête OpenID4VP au système d'exploitation. En réponse, vous recevez un objet de réponse OpenID4VP contenant le TS.43 Digital Credential. Cet identifiant est chiffré et ne peut être déchiffré que par l'agrégateur associé. Après avoir reçu le jeton de l'opérateur, envoyez la réponse de votre application cliente au backend de l'application.

  4. Valider la réponse : dans le backend de votre application, validez la réponse OpenID4VP.

  5. Échanger contre un numéro de téléphone : à partir du backend de votre application, envoyez l'TS.43 Digital Credential à l'agrégateur. L'agrégateur valide l'identifiant et renvoie le numéro de téléphone validé.

Image montrant le flux d'une demande de validation d'un numéro de téléphone
Figure 1.Cycle de vie d'une requête de validation de numéro de téléphone, depuis le backend du validateur qui demande des paramètres à un agrégateur et se terminant par un numéro de téléphone validé renvoyé.

Demander des paramètres DCQL à un agrégateur

À partir du backend de votre application, envoyez une requête à l'agrégateur pour obtenir un objet d'identifiant Digital Credential Query Language (DCQL). Veillez à fournir un nonce et un ID de requête dans votre requête. L'agrégateur renvoie l'objet d'identifiant DCQL, dont la structure est semblable à la suivante :

{
  // 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"]
    }
  ]
}

Créer la requête OpenID4VP

Tout d'abord, à partir du backend de votre application, créez un objet dcql_query en plaçant l'objet d'identifiant DCQL dans un tableau credentials imbriqué dans un objet dcql_query, comme illustré dans l'exemple suivant :

"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"]
        }
      ]
  ]
}

Créez ensuite une requête OpenID4VP avec la structure suivante :

{
  "protocol": "openid4vp-v1-unsigned",
  "data": {
    "response_type": "vp_token",
    "response_mode": "dc_api",
    "nonce": "...",
    "dcql_query": { ... }
  }
}
  • protocol: doit être défini sur openid4vp-v1-unsigned pour les requêtes de validation de numéro de téléphone.
  • response_type et response_mode : constantes indiquant le format de la requête avec les valeurs fixes vp_token et dc_api, respectivement.
  • nonce : valeur unique générée par votre backend pour chaque requête. Le nonce de l'objet d'identifiant DCQL de l'agrégateur doit correspondre à ce nonce.
  • dcql_query : dans ce cas, utilisez dcql_query pour spécifier qu'un TS.43 Digital Credential est demandé. Vous pouvez également demander d'autres identifiants numériques ici.

Encapsulez ensuite la requête OpenID4VP dans un objet de requête d'API DigitalCredential et envoyez-le à l'application cliente.

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

L'extrait suivant montre comment générer la requête d'API DigitalCredential :

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

Appeler l'API Credential Manager

Dans votre application cliente, appelez l'API Credential Manager avec la requête d'API DigitalCredential fournie par le backend de votre application.

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)
  }
}

La réponse de l'API DigitalCredential contient la réponse OpenID4VP. Voici un exemple de JSON d'identifiant type issu du DigitalCredential résultat est comme suit :

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

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

À partir de votre application cliente, renvoyez la réponse de l'API DigitalCredential au serveur backend où elle peut être validée et utilisée pour échanger le numéro de téléphone validé avec un agrégateur.

Dans certains cas, la réponse peut contenir une erreur TS.43. La réponse d'erreur est un objet JSON qui suit le format de réponse d'erreur OpenID4VP :

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

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

Il existe deux valeurs error_code possibles :

  • invalid_request : indique que la requête est mal formée.
  • server_error: indique que le traitement de la requête a échoué. Il peut s'agir d'erreurs locales ou de problèmes TS.43.

Le champ error_description fournit des informations supplémentaires sur le problème.

Valider la réponse de l'identifiant numérique

Voici un exemple d'analyse de la réponse et d'exécution de l'étape de validation dans le backend de votre application :

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.

Échanger contre un numéro de téléphone

À partir du backend de votre application, envoyez le TS.43 Digital Credential validé au point de terminaison de l'agrégateur pour valider l'identifiant et recevoir le numéro de téléphone validé.

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

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