Проверка цифровых удостоверений в приложениях Android может использоваться для аутентификации и авторизации личности пользователя (например, с помощью официального удостоверения личности), подтверждения его свойств (например, с помощью водительского удостоверения, диплома или таких атрибутов, как возраст или адрес) или в других сценариях, когда необходимо выдать и проверить удостоверение, чтобы подтвердить подлинность объекта.
Verifier API поддерживается на устройствах с Android 9 (API уровня 28) и более поздними версиями.
Реализация
Чтобы проверить цифровые удостоверения в проекте для Android, выполните следующие действия:
- Добавьте зависимости в скрипт сборки приложения и инициализируйте класс
CredentialManager. - Создайте запрос цифрового удостоверения личности и используйте его для инициализации
DigitalCredentialOption, а затем создайтеGetCredentialRequest. - Запустите процесс
getCredentialс созданным запросом, чтобы получить успешный ответGetCredentialResponseили обработать возможные исключения. После успешного получения проверьте ответ.
Добавьте зависимости и инициализируйте SDK
Добавьте в скрипт сборки Gradle следующие зависимости:
dependencies {
implementation("androidx.credentials:credentials:1.6.0-beta01")
implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta01")
}
Затем инициализируйте экземпляр класса CredentialManager.
val credentialManager = CredentialManager.create(context)
Как создать запрос цифрового удостоверения
Создайте запрос цифрового удостоверения личности и используйте его для инициализации DigitalCredentialOption.
// The request in the JSON format to conform with
// the JSON-ified Credential Manager - Verifier API request definition.
val requestJson = generateRequestFromServer()
val digitalCredentialOption =
GetDigitalCredentialOption(requestJson = requestJson)
// Use the option from the previous step to build the `GetCredentialRequest`.
val getCredRequest = GetCredentialRequest(
listOf(digitalCredentialOption)
)
Ниже приведен пример запроса OpenId4Vp1. Подробные сведения о полях в этом запросе можно найти в документации по OpenID.
{
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "OD8eP8BYfr0zyhgq4QCVEGN3m7C1Ht_No9H5fG5KJFk",
"dcql_query": {
"credentials": [
{
"id": "cred1",
"format": "mso_mdoc",
"meta": {
"doctype_value": "org.iso.18013.5.1.mDL"
},
"claims": [
{
"path": [
"org.iso.18013.5.1",
"family_name"
]
},
{
"path": [
"org.iso.18013.5.1",
"given_name"
]
},
{
"path": [
"org.iso.18013.5.1",
"age_over_21"
]
}
]
}
]
}
}
}
]
}
Получить сертификат
Запустите процесс getCredential с созданным запросом. Вы получите либо сообщение об успешном выполнении GetCredentialResponse, либо сообщение GetCredentialException, если запрос не удался.
Процесс getCredential запускает системные диалоговые окна Android, в которых пользователю предлагаются доступные варианты учетных данных и собирается информация о его выборе. Затем приложение кошелька, содержащее выбранный вариант удостоверения, отобразит пользовательский интерфейс для получения согласия и выполнения действий, необходимых для создания ответа цифрового удостоверения.
coroutineScope.launch {
try {
val result = credentialManager.getCredential(
context = activityContext,
request = getCredRequest
)
verifyResult(result)
} catch (e : GetCredentialException) {
handleFailure(e)
}
}
// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
val credential = result.credential
when (credential) {
is DigitalCredential -> {
val responseJson = credential.credentialJson
validateResponseOnServer(responseJson)
}
else -> {
// Catch any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential ${credential.type}")
}
}
}
// Handle failure.
fun handleFailure(e: GetCredentialException) {
when (e) {
is GetCredentialCancellationException -> {
// The user intentionally canceled the operation and chose not
// to share the credential.
}
is GetCredentialInterruptedException -> {
// Retry-able error. Consider retrying the call.
}
is NoCredentialException -> {
// No credential was available.
}
else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
}
}
Гибкие запросы нескольких учетных данных
Digital Credentials API также поддерживает запрос нескольких типов удостоверений одновременно с помощью OpenID4VP:
- Одновременные запросы. Запрашивайте несколько учетных данных в рамках одной транзакции пользователя. Например, приложение для путешествий может одновременно запросить посадочный талон и удостоверение личности, чтобы пользователь мог проверить и подтвердить оба документа одним нажатием.
- Гибкое выполнение. Принимайте любые из нескольких типов учетных данных или эмитентов. Например, для подтверждения возраста можно использовать водительское удостоверение или паспорт. Пользователь может выбрать любой из этих документов, если он сохранен в держателе.
Вот пример запроса нескольких учетных данных в одном запросе:
{
"credentials": [
{
"id": "gov_id1",
"format": "mso_mdoc",
"meta": {
"doctype_value": "org.iso.18013.5.1.mDL"
},
"claims": [
{
"path": [
"org.iso.18013.5.1",
"age_over_21"
]
}
]
},
{
"id": "boarding_pass1",
"format": "dc+sd-jwt",
"meta": {
"vct_values": [
"https://credentials.example.airline/boarding-pass"
]
},
"claims": [
{
"path": [
"flight_number"
]
},
{
"path": [
"seat"
]
}
]
}
]
}
Обработка ответного сообщения
После отправки запроса на получение учетных данных обработайте объект GetCredentialResponse, возвращенный методом CredentialManager.
// Handle the successfully returned credential.
fun verifyResult(result: GetCredentialResponse) {
val credential = result.credential
when (credential) {
is DigitalCredential -> {
val responseJson = credential.credentialJson
validateResponseOnServer(responseJson)
}
else -> {
// Catch any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential ${credential.type}")
}
}
}
Обработка исключений
Приложение должно обрабатывать возможные ошибки, в том числе отсутствие учетных данных на устройстве или отмену операции пользователем.
// Handle failure.
fun handleFailure(e: GetCredentialException) {
when (e) {
is GetCredentialCancellationException -> {
// The user intentionally canceled the operation and chose not
// to share the credential.
}
is GetCredentialInterruptedException -> {
// Retry-able error. Consider retrying the call.
}
is NoCredentialException -> {
// No credential was available.
}
is CreateCredentialUnknownException -> {
// An unknown, usually unexpected, error has occurred. Check the
// message error for any additional debugging information.
}
is CreateCredentialCustomException -> {
// You have encountered a custom error thrown by the wallet.
// If you made the API call with a request object that's a
// subclass of CreateCustomCredentialRequest using a 3rd-party SDK,
// then you should check for any custom exception type constants
// within that SDK to match with e.type. Otherwise, drop or log the
// exception.
}
else -> Log.w(TAG, "Unexpected exception type ${e::class.java}")
}
}