Dodawanie zależności

W pliku build.gradle aplikacji dodaj te zależności dla Credential Manager:

Kotlin

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

Groovy

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

Inicjowanie Credential Manager API

Użyj aplikacji lub kontekstu aktywności, aby utworzyć obiekt CredentialManager.

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

Tworzenie żądania danych logowania w formie cyfrowej

Aby poprosić o zweryfikowany adres e-mail, utwórz GetCredentialRequest zawierający GetDigitalCredentialOption. Ta opcja wymaga ciągu tekstowego w formacie żądania OpenID for Verifiable Presentations (OpenID4VP).requestJson

Żądanie OpenID4VP w formacie JSON musi mieć określoną strukturę. Obecni dostawcy obsługują strukturę JSON z zewnętrznym elementem opakowującym "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))

Następnie umieść openId4vpRequest JSON w GetDigitalCredentialOption, utwórz GetCredentialRequest i wywołaj getCredential().

Wyświetlanie prośby użytkownikowi

Wyświetl użytkownikowi prośbę za pomocą wbudowanego interfejsu Menedżera danych logowania.

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
}

Analizowanie odpowiedzi na urządzeniu klienta

Po otrzymaniu odpowiedzi możesz wstępnie przeanalizować ją na urządzeniu klienta. Jest to przydatne, gdy chcesz natychmiast zaktualizować interfejs, np. wyświetlić nazwę użytkownika.

Poniższy kod wyodrębnia surowy token JWT z selektywnym ujawnianiem informacji (SD-JWT) i używa funkcji pomocniczej do dekodowania jego deklaracji.

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

Obsługa odpowiedzi

Interfejs Credential Manager API zwróci odpowiedź DigitalCredential.

Poniżej znajdziesz przykład surowego tokena responseJsonString oraz danych po przeanalizowaniu wewnętrznego tokena SD-JWT, w którym oprócz zweryfikowanego adresu e-mail uzyskasz też dodatkowe metadane:

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

Weryfikacja po stronie serwera podczas tworzenia konta

Aby przeprowadzić weryfikację, aplikacja musi wysłać pełny ciąg responseJsonString na serwer w celu kryptograficznego sprawdzenia poprawności, zanim utworzy konto lub zaloguje użytkownika.

Weryfikacja na serwerze musi spełniać te warunki:

  • Weryfikacja wystawcy: sprawdź, czy pole iss (wystawca) jest zgodne z https://verifiablecredentials-pa.googleapis.com.
  • Weryfikacja podpisu: sprawdź podpis SD-JWT za pomocą kluczy publicznych (JWK) dostępnych na stronie https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.

Aby zapewnić pełne bezpieczeństwo, sprawdź też nonce, aby zapobiec atakom typu replay.

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
}

Opcjonalnym, ale wysoce zalecanym kolejnym krokiem po utworzeniu konta jest natychmiastowe utworzenie klucza dostępu do tego konta. Zapewnia to bezpieczną metodę logowania bez hasła. Ten proces jest identyczny ze standardową rejestracją klucza dostępu.

Aby proces działał w WebView, deweloperzy powinni wdrożyć most JavaScript (JS Bridge), który ułatwi przekazywanie danych. Ten pomost umożliwia widokowi WebView wysyłanie sygnałów do aplikacji natywnej, która może następnie wykonać rzeczywiste wywołanie interfejsu Credential Manager API.