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

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

Котлин

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

Круто

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

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

Используйте контекст вашего приложения или действия для создания объекта 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).

Запрос OpenID4VP в формате JSON должен соответствовать определённой структуре. Существующие провайдеры поддерживают структуру 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() .

Предъявите запрос пользователю.

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

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
}

Проанализируйте ответ на стороне клиента.

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

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

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

Обработайте ответ

API диспетчера учетных данных вернет ответ DigitalCredential .

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

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

Серверная проверка данных при создании учетной записи

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

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

  • Проверка эмитента : Убедитесь, что поле 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 передавать сигнал нативному приложению, которое затем может выполнить фактический вызов API диспетчера учетных данных.