Обзор

Добавьте зависимости

В файле build.gradle своего приложения добавьте следующие зависимости для Менеджера учетных данных:

Котлин

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

Круто

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

Инициализация Менеджера учетных данных

Используйте контекст приложения или действия, чтобы создать объект CredentialManager.

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

Как создать запрос цифрового удостоверения

Чтобы запросить подтвержденный адрес электронной почты, создайте GetCredentialRequest, содержащий GetDigitalCredentialOption. Для этого варианта требуется строка requestJson, отформатированная как запрос OpenID для проверяемых презентаций (OpenID4VP).

JSON-запрос OpenID4VP должен иметь определенную структуру. Текущие поставщики поддерживают структуру JSON с внешней оболочкой "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))

Затем оберните JSON-код openId4vpRequest в GetDigitalCredentialOption, создайте GetCredentialRequest и вызовите getCredential().

Покажите запрос пользователю

Покажите пользователю запрос, используя встроенный интерфейс Менеджера учетных данных.

coroutineScope {
    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
    }
}

Обработка ответа на стороне клиента

Получив ответ, вы можете выполнить предварительный синтаксический анализ на клиенте. Это полезно для немедленного обновления пользовательского интерфейса, например, путем отображения имени пользователя.

Приведенный ниже код извлекает необработанный веб-токен JSON с выборочным раскрытием (SD-JWT) и использует вспомогательный инструмент для декодирования утверждений.

// 1. Parse the outer JSON wrapper to get the `vp_token`
val responseData = JSONObject(responseJsonString)
val dataObject = responseData.getJSONObject("data")
val vpToken = dataObject.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"))
)

Обработка ответного сообщения

Credential Manager API вернет ответ DigitalCredential.

Ниже показано, как выглядит необработанный responseJsonString и как выглядят утверждения после синтаксического анализа внутреннего SD-JWT, где вы получаете дополнительные метаданные вместе с подтвержденным адресом электронной почты:

/*
// Example of the raw JSON response from credential.credentialJson:
{
  "protocol": "openid4vp-v1-unsigned",
  "data": {
    "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": ""
}
 */

Проверка на стороне сервера при создании аккаунта

Для проверки приложение должно отправить полный объект responseJsonString на ваш сервер для криптографической валидации, прежде чем создавать аккаунт или выполнять вход пользователя.

  • Подлинность данных. Проверка URL эмитента (iss) и подписи SD-JWT подтверждает, что эти данные были выданы доверенным органом.

Проверка на сервере должна обеспечивать следующее:

  • Проверьте издателя. Убедитесь, что поле iss (издатель) совпадает с https://verifiablecredentials-pa.googleapis.com.
  • Проверка подписи. Проверьте подпись SD-JWT с помощью открытых ключей (JWK), доступных по адресу https://verifiablecredentials-pa.googleapis.com/.well-known/vc-public-jwks.

Чтобы обеспечить полную безопасность, также проверяйте nonce. Это поможет предотвратить атаки повторного воспроизведения.

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
}

После инициализации аккаунта настоятельно рекомендуется сразу же создать для него ключ доступа. Это безопасный способ входа без пароля. Этот процесс идентичен стандартной регистрации ключа доступа.

Чтобы процесс работал в WebView, разработчикам нужно реализовать мост JavaScript (JS Bridge) для передачи данных. Этот мост позволяет объекту WebView отправлять сигнал нативному приложению, которое затем может выполнить фактический вызов Credential Manager API.