Verifica números de teléfono con credenciales digitales

En esta guía, se detalla cómo usar la API de DigitalCredential para obtener números de teléfono verificados de tus usuarios. El proceso consta de dos pasos:

  1. Solicita un TS.43 token: Tu app cliente (el "verificador") solicita un token TS.43 temporal desde el dispositivo del usuario. El TS.43 token es una credencial emitida por el operador que representa la identidad del usuario.
  2. Intercambia el token por un número de teléfono: El backend de tu app intercambia el TS.43 token con un agregador o un operador para obtener el número de teléfono verificado del usuario.

Requisitos previos

Para implementar la verificación del número de teléfono con la API de DigitalCredential, necesitas una cuenta con un agregador. Un agregador interactúa con los operadores y proporciona la superficie de API necesaria para tu app, por lo general, como un extremo de API de Cloud facturable.

También debes agregar las siguientes dependencias a tu secuencia de comandos de compilación de Gradle:

Kotlin

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

Groovy

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

Implementación

Por lo general, el proceso integral sigue estos pasos:

  1. Solicita parámetros de DCQL (lenguaje de consulta de credenciales digitales) a un agregador: Llama a uno o más agregadores y solicita un conjunto de parámetros de DCQL. DCQL te permite especificar las credenciales digitales exactas que necesitas de cada agregador.
  2. Crea la solicitud de OpenID4VP: Desde el backend de tu app, crea la solicitud de OpenID4VP y, al mismo tiempo, incluye los parámetros de DCQL del agregador. Luego, envía la solicitud de OpenID4VP a tu app cliente.

  3. Llama a la API de Credential Manager: En tu app cliente, usa la API de Credential Manager para enviar la solicitud de OpenID4VP al sistema operativo. En respuesta, recibirás un objeto de respuesta OpenID4VP que contiene el TS.43 Digital Credential. Esta credencial está encriptada y solo el agregador asociado puede desencriptarla. Después de recibir el token de la empresa de transporte, envía la respuesta de tu app cliente al backend de la app.

  4. Valida la respuesta: En el backend de tu app, valida la respuesta de OpenID4VP.

  5. Intercambio por número de teléfono: Desde el backend de tu app, envía el TS.43 Digital Credential al agregador. El agregador valida la credencial y devuelve el número de teléfono verificado.

Imagen que muestra el flujo de una solicitud de verificación del número de teléfono
Figura 1: Ciclo de vida de una solicitud de verificación de número de teléfono, desde que el backend del verificador solicita parámetros a un agregador hasta que se devuelve un número de teléfono verificado.

Solicita parámetros de DCQL a un agregador

Desde el backend de tu app, envía una solicitud al agregador para obtener un objeto de credencial del lenguaje de consultas de credenciales digitales (DCQL). Asegúrate de proporcionar un nonce y un ID de solicitud en tu solicitud. El agregador devuelve el objeto de credencial de DCQL, que tiene una estructura similar a la siguiente:

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

Crea la solicitud de OpenID4VP

Primero, desde el backend de tu app, crea un objeto dcql_query colocando el objeto de credenciales de DCQL en un array credentials anidado dentro de un objeto dcql_query, como se muestra en el siguiente ejemplo:

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

Luego, crea una solicitud de OpenID4VP con la siguiente estructura:

{
  "protocol": "openid4vp-v1-unsigned",
  "data": {
    "response_type": "vp_token",
    "response_mode": "dc_api",
    "nonce": "...",
    "dcql_query": { ... }
  }
}
  • protocol: Debe establecerse en openid4vp-v1-unsigned para las solicitudes de verificación de números de teléfono.
  • response_type y response_mode: Son constantes que denotan el formato de la solicitud con valores fijos vp_token y dc_api, respectivamente.
  • nonce: Es un valor único que genera tu backend para cada solicitud. El valor de nonce en el objeto de credencial de DCQL del agregador debe coincidir con este valor de nonce.
  • dcql_query: En este caso, usa dcql_query para especificar que se solicita un TS.43 Digital Credential. También puedes solicitar otras credenciales digitales aquí.

Luego, encapsula la solicitud de OpenID4VP en un objeto de solicitud de la API de DigitalCredential y envíalo a la app cliente.

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

En el siguiente fragmento, se muestra cómo generar la solicitud de la API de 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

Llama a la API de Credential Manager

En tu app cliente, llama a la API de Credential Manager con la solicitud de la API de DigitalCredential que proporciona el backend de tu app.

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 respuesta de la API de DigitalCredential contiene la respuesta de OpenID4VP. Un archivo JSON de credenciales típico del resultado de DigitalCredential es el siguiente:

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

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

Desde tu app cliente, envía la respuesta de la API de DigitalCredential al servidor de backend, donde se puede validar y usar para intercambiar el número de teléfono verificado con un agregador.

Valida la respuesta de la credencial digital

A continuación, se muestra un ejemplo de cómo analizar la respuesta y realizar el paso de validación en el backend de tu app:

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.

Intercambio por número de teléfono

Desde el backend de tu app, envía el TS.43 Digital Credential validado al extremo del agregador para validar la credencial y recibir el número de teléfono verificado.

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

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