E-Mail-Bestätigung implementieren

In diesem Leitfaden wird beschrieben, wie Sie den Abruf bestätigter E‑Mail-Adressen mithilfe der Digital Credentials Verifier API über eine OpenID for Verifiable Presentations (OpenID4VP)-Anfrage implementieren.

Abhängigkeiten hinzufügen

Fügen Sie in der Datei build.gradle Ihrer App die folgenden Abhängigkeiten für den Credential Manager hinzu:

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

Credential Manager initialisieren

Verwenden Sie den Kontext Ihrer App oder Aktivität, um ein CredentialManager-Objekt zu erstellen.

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

Anfrage für digitale Anmeldedaten erstellen

Wenn Sie eine bestätigte E‑Mail-Adresse anfordern möchten, erstellen Sie eine GetCredentialRequest, die eine GetDigitalCredentialOption enthält. Für diese Option ist ein requestJson-String erforderlich, der als OpenID for Verifiable Presentations-Anfrage (OpenID4VP) formatiert ist.

Die JSON-Datei der OpenID4VP-Anfrage muss einer bestimmten Struktur folgen. Die aktuellen Anbieter unterstützen eine JSON-Struktur mit einem äußeren Wrapper „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))

Die Anfrage enthält die folgenden wichtigen Informationen:

  • DCQL-Abfrage: Die dcql_query gibt den Typ der Anmeldedaten und die angeforderten Anforderungen (email_verified) an. Sie können andere Anforderungen anfordern, um die Überprüfungsstufe zu bestimmen. Hier sind einige mögliche Ansprüche:
    • email_verified: In der Antwort ist dies ein boolescher Wert, der angibt, ob die E-Mail-Adresse bestätigt ist.
    • hd (gehostete Domain): In der Antwort ist dieses Feld leer.
  • nonce: Für jede Anfrage wird ein eindeutiger, kryptografisch sicherer Zufallswert generiert. Dies ist für die Sicherheit von entscheidender Bedeutung, da so Replay-Angriffe verhindert werden.
  • UserInfoCredential: Dieser Wert impliziert einen bestimmten Typ von digitalem Ausweis, der Nutzerattribute enthält. Die Angabe dieses Parameters in der Anfrage ist entscheidend, um den Anwendungsfall der E‑Mail-Bestätigung zu unterscheiden.

Als Nächstes verpacken Sie das openId4vpRequest-JSON in ein GetDigitalCredentialOption, erstellen ein GetCredentialRequest und rufen getCredential() auf.

Anfrage an den Nutzer stellen

Präsentieren Sie dem Nutzer die Anfrage über die integrierte Benutzeroberfläche des Credential Managers.

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
}

Antwort auf dem Client parsen

Nachdem Sie die Antwort erhalten haben, können Sie auf dem Client ein vorläufiges Parsing durchführen. Das ist nützlich, um die Benutzeroberfläche sofort zu aktualisieren, z. B. durch die Anzeige des Nutzernamens.

Mit dem folgenden Code wird das rohe Selective Disclosure JWT (SD-JWT) extrahiert und seine Anforderungen werden mit einem Hilfsprogramm decodiert.

// 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"))
)

Antwort verarbeiten

Die Credential Manager API gibt die Antwort DigitalCredential zurück.

Im Folgenden sehen Sie ein Beispiel dafür, wie das Roh-responseJsonString aussieht und wie die Anforderungen nach dem Parsen des inneren SD-JWT aussehen, wobei Sie zusätzlich zur bestätigten E‑Mail-Adresse weitere Metadaten erhalten:

/*
// 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": ""
}
 */

Serverseitige Validierung für die Kontoerstellung

Da die abgerufene E-Mail-Adresse kryptografisch verifiziert ist, können Sie den Schritt zur Bestätigung der E-Mail-Adresse per OTP weglassen. Dadurch wird die Registrierung erheblich vereinfacht und die Conversion-Rate kann steigen. Dieser Vorgang wird am besten auf Ihrem Server ausgeführt. Der Client sendet die Rohantwort (mit dem vp_token) und die ursprüngliche Nonce an einen neuen Serverendpunkt.

Zur Überprüfung muss Ihre Anwendung die vollständige responseJsonString an Ihren Server senden, damit sie kryptografisch validiert werden kann, bevor ein Konto erstellt oder der Nutzer angemeldet wird.

Der digitale Berechtigungsnachweis bietet zwei wichtige Bestätigungsebenen für Ihren Server:

  • Authentizität der Daten: Durch die Überprüfung der Aussteller-URL (iss) und der SD-JWT-Signatur wird nachgewiesen, dass diese Daten von einer vertrauenswürdigen Stelle ausgestellt wurden.
  • Identität des Präsentierenden: Durch die Überprüfung des Felds cnf und der Signatur der Schlüsselbindung (kb) wird bestätigt, dass der Berechtigungsnachweis von demselben Gerät geteilt wird, für das er ursprünglich ausgestellt wurde. Dadurch wird verhindert, dass er abgefangen oder auf einem anderen Gerät verwendet wird.

Die Validierung auf dem Server muss Folgendes erreichen:

  • Aussteller prüfen: Das Feld iss (Aussteller) muss mit https://verifiablecredentials-pa.googleapis.com übereinstimmen.
  • Signatur prüfen: Prüfen Sie die Signatur des SD-JWT mit den öffentlichen Schlüsseln (JWKs), die unter https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks verfügbar sind.

Für vollständige Sicherheit sollten Sie auch die nonce validieren, um Replay-Angriffe zu verhindern.

Durch die Kombination dieser Schritte kann Ihr Server sowohl die Authentizität der Daten als auch die Identität des Inhabers validieren. So wird sichergestellt, dass der Berechtigungsnachweis vor der Bereitstellung des neuen Kontos nicht abgefangen oder manipuliert wurde.

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
}

Passkey-Erstellung

Ein optionaler, aber dringend empfohlener nächster Schritt nach der Bereitstellung eines Kontos ist das sofortige Erstellen eines Passkeys für dieses Konto. So können sich Nutzer sicher und ohne Passwort anmelden. Dieser Ablauf ist mit einer Standard-Passkey-Registrierung identisch.

WebView-Unterstützung

Damit der Ablauf auf einem WebView funktioniert, sollten Entwickler eine JavaScript-Bridge (JS-Bridge) implementieren, um die Übergabe zu ermöglichen. Über diese Brücke kann das WebView-Objekt der systemeigenen App signalisieren, dass sie den eigentlichen Aufruf der Credential Manager API ausführen kann.

Weitere Informationen