Implementare la verifica dell'indirizzo email con l'API Digital Credentials

Aggiungi dipendenze

Nel file build.gradle dell'app, aggiungi le seguenti dipendenze per Credential Manager:

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

Inizializza Gestore delle credenziali

Utilizza il contesto dell'app o dell'attività per creare un oggetto CredentialManager.

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)

Crea la richiesta di credenziali digitali

Per richiedere un'email verificata, crea un GetCredentialRequest contenente un GetDigitalCredentialOption. Questa opzione richiede una stringa requestJson formattata come richiesta OpenID for Verifiable Presentations (OpenID4VP).

Il JSON della richiesta OpenID4VP deve seguire una struttura specifica. I provider attuali supportano una struttura JSON con un wrapper esterno "digital": {"requests": [...]}.

    val nonce = generateSecureRandomNonce()

    // This request follows the OpenID4VP spec
    val openId4vpRequest = """
{
  "requests": [
    {
      "protocol": "openid4vp-v1-unsigned",
      "data": {
        "response_type": "vp_token",
        "response_mode": "dc_api",
        "nonce": "$nonce",
        "dcql_query": {
          "credentials": [
            {
              "id": "user_info_query",
              "format": "dc+sd-jwt",
               "meta": { 
                  "vct_values": ["UserInfoCredential"] 
               },
              "claims": [ 
                {"path": ["email"]}, 
                {"path": ["name"]},  
                {"path": ["given_name"]},
                {"path": ["family_name"]},
                {"path": ["picture"]},
                {"path": ["hd"]},
                {"path": ["email_verified"]}
              ]
            }
          ]
        }
      }
    }
  ]
}
"""

    val getDigitalCredentialOption = GetDigitalCredentialOption(requestJson = openId4vpRequest)
    val request = GetCredentialRequest(listOf(getDigitalCredentialOption))

Successivamente, inserisci il JSON openId4vpRequest in una GetDigitalCredentialOption, crea una GetCredentialRequest e chiama getCredential().

Presenta la richiesta all'utente

Presenta la richiesta all'utente utilizzando l'UI integrata di Gestore delle credenziali.

try {
    // Requesting Digital Credential from user...
    val result = credentialManager.getCredential(activity, request)

    when (val credential = result.credential) {
        is DigitalCredential -> {
            val responseJsonString = credential.credentialJson

            // Successfully received digital credential response.

            // Next, parse this response and send it to your server.
            // ...
        }

        else -> {
            // handle Unexpected State() - Up to the developer
        }
    }
} catch (e: Exception) {
    // handle exceptions - Up to the developer
}

Analizza la risposta sul client

Dopo aver ricevuto la risposta, puoi eseguire un'analisi preliminare sul client. Questa operazione è utile per aggiornare immediatamente l'UI, ad esempio mostrando il nome dell'utente.

Il seguente codice estrae il JWT di divulgazione selettiva (SD-JWT) non elaborato e utilizza un helper per decodificare le relative attestazioni.

// 1. Parse the outer JSON wrapper to get the `vp_token`
val responseData = JSONObject(responseJsonString)
val vpToken = responseData.getJSONObject("vp_token")

// 2. Extract the raw SD-JWT string
val credentialId = vpToken.keys().next()
val rawSdJwt = vpToken.getJSONArray(credentialId).getString(0)

// 3. Use your parser to get the verified claims
// Server-side validation/parsing is highly recommended.

// Assumes a local parser like the one in our SdJwtParser.kt sample
val claims = SdJwtParser.parse(rawSdJwt)
Log.d("TAG", "Parsed Claims: ${claims.toString(2)}")

// 4. Create your VerifiedUserInfo object with REAL data
val userInfo = VerifiedUserInfo(
    email = claims.getString("email"),
    displayName = claims.optString("name", claims.getString("email"))
)

Gestisci la risposta

L'API Credential Manager restituirà una DigitalCredential risposta.

Di seguito è riportato un esempio di come appare la responseJsonString non elaborata e di come appaiono le attestazioni dopo l'analisi dell'SD-JWT interno, in cui ottieni anche metadati aggiuntivi insieme all'indirizzo email verificato:

/*
// Example of the raw JSON response from credential.credentialJson:
{
  "vp_token": {
    // This key matches the 'id' you set in your dcql_query
    "user_info_query": [
      // The SD-JWT string (Issuer JWT ~ Disclosures ~ Key Binding JWT)
      "eyJhbGciOiJ...~WyI...IiwgImVtYWlsIiwgInVzZXJAZXhhbXBsZS5jb20iXQ~...~eyJhbGciOiJ..."
    ]
  }
}

// Example of the parsed and verified claims from the SD-JWT on your server:
{
  "cnf": {
    "jwk": {..}
  },
  "exp": 1775688222,
  "iat": 1775083422,
  "iss": "https://verifiablecredentials-pa.googleapis.com",
  "vct": "UserInfoCredential",
  "email": "jane.doe.246745@gmail.com",
  "email_verified": true,
  "given_name": "Jane",
  "family_name": "Doe",
  "name": "Jane Doe",
  "picture": "http://example.com/janedoe/me.jpg",
  "hd": ""
}
 */

Convalida lato server per la creazione dell'account

Per la verifica, la tua applicazione deve inviare l'intera responseJsonString al server per la convalida crittografica prima di creare un account o di consentire l'accesso all'utente.

  • Autenticità dei dati: la verifica dell'URL dell'emittente (iss) e della SD-JWT dimostra che un'autorità attendibile ha emesso questi dati.

La convalida sul server deve raggiungere i seguenti obiettivi:

  • Verifica l'emittente: assicurati che il campo iss (emittente) corrisponda a https://verifiablecredentials-pa.googleapis.com.
  • Verifica la firma: controlla la firma dell'SD-JWT utilizzando le chiavi pubbliche (JWK) disponibili all'indirizzo https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.

Per una sicurezza completa, assicurati di convalidare anche il nonce per impedire gli attacchi di riproduzione.

try {
    // Send the raw credential response and the original nonce to your server.
    // Your server must validate the response. createAccountWithVerifiedCredentials
    // is a custom implementation per each RP for server side verification and account creation.
    val serverResponse = createAccountWithVerifiedCredentials(responseJsonString, nonce)

    // Server returns the new account info (e.g., email, name)
    val claims = JSONObject(serverResponse.json)

    val userInfo = VerifiedUserInfo(
        email = claims.getString("email"),
        displayName = claims.optString("name", claims.getString("email"))
    )

    // handle response - Up to the developer
} catch (e: Exception) {
    // handle exceptions - Up to the developer
}

Un passaggio successivo facoltativo, ma altamente consigliato, dopo il provisioning di un account è creare immediatamente una passkey per quell'account. In questo modo, l'utente può accedere in modo sicuro e senza password. Questo flusso è identico a una registrazione standard della passkey.

Affinché il flusso funzioni su una WebView, gli sviluppatori devono implementare un bridge JavaScript (JS Bridge) per facilitare il trasferimento. Questo bridge consente all'oggetto WebView di segnalare l'app nativa, che può quindi eseguire la chiamata effettiva all'API Credential Manager.