Implementa la verifica del numero di telefono

Questa guida spiega in dettaglio come utilizzare l'API DigitalCredential per ottenere numeri di telefono verificati per i tuoi utenti. La procedura prevede due passaggi:

  1. Controllo preliminare: verifica se l'operatore dell'utente supporta questa API eseguendo una query sull'API Android Telephony. Se l'operatore non è supportato, torna al normale flusso di SMS.
  2. Richiedi un TS.43 token: l'app client (il "verificatore") richiede un token TS.43 temporaneo dal dispositivo dell'utente. TS.43 token è una credenziale emessa dall'operatore che rappresenta l'identità dell'utente.
  3. Scambia il token con un numero di telefono: il backend della tua app scambia il token TS.43 token con un aggregatore o un operatore per ottenere il numero di telefono verificato dell'utente.

Compatibilità con le versioni di Android

L'API Verifica del numero di telefono è supportata su Android 10 (livello API 29) e versioni successive.

Prerequisiti

Per implementare la verifica del numero di telefono con l'API DigitalCredential, devi avere un account con un aggregatore o un operatore di telefonia mobile. Un aggregatore interagisce con gli operatori e fornisce la superficie API necessaria per la tua app, di solito come endpoint API Cloud fatturabile. In questa guida, sia gli aggregatori che gli operatori sono indicati collettivamente come aggregatori.

Devi anche aggiungere le seguenti dipendenze allo script di build Gradle:

Kotlin

dependencies {
    implementation("androidx.credentials:credentials:1.7.0-alpha02")
    implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha02")
}

Trendy

dependencies {
    implementation "androidx.credentials:credentials:1.7.0-alpha02"
    implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha02"
}

Implementazione

Prima di chiamare l'API Digital Credentials, controlla se l'operatore dell'utente supporta questa funzionalità utilizzando l'API Android Telephony.

Aggiungi l'autorizzazione READ_PHONE_STATE al file AndroidManifest.xml.

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

Con questo ID, verifica il supporto di TS.43 con il backend dell'aggregatore. Se l'operatore di questo utente non è supportato, puoi utilizzare un metodo alternativo, come l'SMS, poiché la chiamata all'API Digital Credentials restituisce un risultato vuoto. In caso contrario, continua con il flusso delle credenziali digitali.

La procedura end-to-end prevede in genere i seguenti passaggi:

  1. Richiedi i parametri DCQL (Digital Credential Query Language) da un aggregatore: chiama uno o più aggregatori e richiedi un insieme di parametri DCQL. DCQL ti consente di specificare le credenziali digitali esatte di cui hai bisogno da ogni aggregatore.
  2. Crea la richiesta OpenID4VP: dal backend della tua app, crea la richiesta OpenID4VP, includendo i parametri DCQL dell'aggregatore. Poi, invia la richiesta OpenID4VP alla tua app client.

  3. Chiama l'API Credential Manager: nell'app client, utilizza l'API Credential Manager per inviare la richiesta OpenID4VP al sistema operativo. In risposta, ricevi un oggetto di risposta OpenID4VP contenente TS.43 Digital Credential. Questa credenziale è criptata e può essere decriptata solo dall'aggregatore associato. Dopo aver ricevuto il token dell'operatore, invia la risposta dall'app client al backend dell'app.

  4. Convalida la risposta: nel backend dell'app, convalida la risposta OpenID4VP.

  5. Scambio con il numero di telefono: dal backend della tua app, invia il TS.43 Digital Credential all'aggregatore. L'aggregatore convalida la credenziale e restituisce il numero di telefono verificato.

Immagine che mostra il flusso di una richiesta di verifica del numero di telefono
Figura 1.Il ciclo di vita di una richiesta di verifica del numero di telefono, a partire dal backend del Verifier che richiede i parametri a un aggregatore e termina con la restituzione di un numero di telefono verificato.

Richiedi i parametri DCQL a un aggregatore

Dal backend della tua app, invia una richiesta all'aggregatore per un oggetto credenziale DCQL (Digital Credential Query Language). Assicurati di fornire un nonce e un ID richiesta nella richiesta. L'aggregatore restituisce l'oggetto delle credenziali DCQL, che ha una struttura simile alla seguente:

{
  // 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 richiesta OpenID4VP

Innanzitutto, dal backend della tua app, crea un oggetto dcql_query inserendo l'oggetto credenziale DCQL in un array credentials nidificato all'interno di un oggetto dcql_query, come mostrato nell'esempio seguente:

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

Poi, crea una richiesta OpenID4VP con la seguente struttura:

{
  "protocol": "openid4vp-v1-unsigned",
  "data": {
    "response_type": "vp_token",
    "response_mode": "dc_api",
    "nonce": "...",
    "dcql_query": { ... }
  }
}
  • protocol: deve essere impostato su openid4vp-v1-unsigned per le richieste di verifica del numero di telefono.
  • response_type e response_mode: costanti che indicano il formato della richiesta con valori fissi rispettivamente vp_token e dc_api.
  • nonce: un valore univoco generato dal tuo backend per ogni richiesta. Il nonce nell'oggetto delle credenziali DCQL dell'aggregatore deve corrispondere a questo nonce.
  • dcql_query: in questo caso, utilizza dcql_query per specificare che è in corso la richiesta di un TS.43 Digital Credential. Puoi anche richiedere altre credenziali digitali qui.

Poi, inserisci la richiesta OpenID4VP in un oggetto di richiesta dell'API DigitalCredential e inviala all'app client.

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

Il seguente snippet mostra come generare la richiesta 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

Chiama l'API Credential Manager

Nella tua app client, effettua una chiamata all'API Credential Manager con la richiesta dell'API DigitalCredential fornita dal backend della tua 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 risposta dell'API DigitalCredential contiene la risposta OpenID4VP. Un tipico file JSON delle credenziali del risultato DigitalCredential è il seguente:

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

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

Dall'app client, invia la risposta dell'API DigitalCredential al server di backend, dove può essere convalidata e utilizzata per lo scambio con il numero di telefono verificato con un aggregatore.

In alcuni casi, la risposta potrebbe contenere un errore TS.43. La risposta di errore è un oggetto JSON che segue il formato della risposta di errore OpenID4VP:

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

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

I valori error_code possibili sono 2:

  • invalid_request: indica che la richiesta non è stata formattata correttamente.
  • server_error: indica che si sono verificati errori durante l'elaborazione della richiesta. Potrebbe trattarsi di errori locali o di problemi relativi a TS.43.

Il campo error_description fornisce ulteriori dettagli sul problema.

Convalida la risposta della credenziale digitale

Di seguito è riportato un esempio di come analizzare la risposta ed eseguire il passaggio di convalida nel backend della tua 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.

Scambia con un numero di telefono

Dal backend della tua app, invia il TS.43 Digital Credential convalidato all'endpoint dell'aggregatore per convalidare la credenziale e ricevere il numero di telefono verificato.

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

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