Verificar números de telefone com credenciais digitais

Este guia detalha como usar a API DigitalCredential para receber números de telefone verificados dos seus usuários. O processo envolve duas etapas:

  1. Solicitar um TS.43 token: o app cliente (o "verificador") solicita um token temporário TS.43 do dispositivo do usuário. O TS.43 token é uma credencial emitida pela operadora que representa a identidade do usuário.
  2. Troque o token por um número de telefone: o back-end do seu app troca o TS.43 token com um agregador ou uma operadora pelo número de telefone verificado do usuário.

Pré-requisitos

Para implementar a verificação de número de telefone com a API DigitalCredential, você precisa de uma conta com um agregador. Um agregador interage com as operadoras e fornece a superfície de API necessária para seu app, geralmente como um endpoint de API de nuvem faturável.

Você também precisa adicionar as seguintes dependências ao script de build do 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"
}

Implementação

O processo de ponta a ponta geralmente segue estas etapas:

  1. Solicitar parâmetros de DCQL (Digital Credential Query Language) de um agregador: chame um ou mais agregadores e solicite um conjunto de parâmetros de DCQL. Com a DCQL, é possível especificar as credenciais digitais exatas necessárias de cada agregador.
  2. Crie a solicitação OpenID4VP: no back-end do app, crie a solicitação OpenID4VP e inclua os parâmetros de DCQL do agregador. Em seguida, envie a solicitação OpenID4VP para o app cliente.

  3. Chame a API Credential Manager: no app cliente, use a API Credential Manager para enviar a solicitação OpenID4VP ao sistema operacional. Em resposta, você recebe um objeto de resposta OpenID4VP que contém o TS.43 Digital Credential. Essa credencial é criptografada e só pode ser descriptografada pelo agregador associado. Depois de receber o token da transportadora, envie a resposta do app cliente para o back-end do app.

  4. Valide a resposta: no back-end do app, valide a resposta OpenID4VP.

  5. Trocar por número de telefone: no back-end do app, envie o TS.43 Digital Credential ao agregador. O agregador valida a credencial e retorna o número de telefone verificado.

Imagem mostrando o fluxo de uma solicitação de verificação de número de telefone
Figura 1.O ciclo de vida de uma solicitação de verificação de número de telefone, começando com o back-end do verificador solicitando parâmetros de um agregador e terminando com um número de telefone verificado retornado.

Solicitar parâmetros de DCQL de um agregador

No back-end do app, envie uma solicitação ao agregador para um objeto de credencial da Digital Credential Query Language (DCQL). Forneça um uso único e um ID de solicitação na sua solicitação. O agregador retorna o objeto de credencial da DCQL, que tem uma estrutura semelhante a esta:

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

Criar a solicitação OpenID4VP

Primeiro, no back-end do app, crie um objeto dcql_query colocando o objeto de credencial da DCQL em uma matriz credentials aninhada em um objeto dcql_query conforme mostrado no exemplo a seguir:

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

Em seguida, crie uma solicitação OpenID4VP com a seguinte estrutura:

{
  "protocol": "openid4vp-v1-unsigned",
  "data": {
    "response_type": "vp_token",
    "response_mode": "dc_api",
    "nonce": "...",
    "dcql_query": { ... }
  }
}
  • protocol: precisa ser definido como openid4vp-v1-unsigned para solicitações de verificação de número de telefone.
  • response_type e response_mode: constantes que indicam o formato da solicitação com valores fixos vp_token e dc_api, respectivamente.
  • nonce: um valor exclusivo gerado pelo seu back-end para cada solicitação. O nonce no objeto de credencial DCQL do agregador precisa corresponder a este nonce.
  • dcql_query: nesse caso, use dcql_query para especificar que um TS.43 Digital Credential está sendo solicitado. Você também pode solicitar outras credenciais digitais aqui.

Em seguida, encapsule a solicitação OpenID4VP em um objeto de solicitação da API DigitalCredential e envie para o app cliente.

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

O snippet a seguir demonstra como gerar a solicitação da 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

Chamar a API Credential Manager

No app cliente, faça uma chamada para a API Credential Manager com a solicitação da API DigitalCredential fornecida pelo back-end do 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)
  }
}

A resposta da API DigitalCredential contém a resposta do OpenID4VP. Um JSON de credencial típico do resultado DigitalCredential é assim:

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

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

Do app cliente, envie a resposta da API DigitalCredential de volta para o servidor de back-end, onde ela pode ser validada e usada para trocar pelo número de telefone verificado com um agregador.

Validar a resposta da credencial digital

Confira um exemplo de como analisar a resposta e realizar a etapa de validação no back-end do 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.

Troca por número de telefone

No back-end do app, envie o TS.43 Digital Credential validado para o endpoint do agregador, valide a credencial e receba o número de telefone verificado.

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

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