Прежде чем ваши пользователи смогут пройти аутентификацию с помощью паролей, ваше приложение должно сначала зарегистрировать или создать пароль для их учетной записи.
Чтобы создать ключ доступа, получите необходимые данные с сервера приложений, а затем вызовите API диспетчера учётных данных, который вернёт пару открытого и закрытого ключей. Возвращённый закрытый ключ хранится у поставщика учётных данных, например, в диспетчере паролей Google, в качестве ключа доступа. Открытый ключ хранится на сервере приложений.

Предпосылки
Убедитесь, что вы настроили ссылки на цифровые активы и что вы ориентируетесь на устройства под управлением Android 9 (уровень API 28) или выше.
Обзор
В этом руководстве рассматриваются изменения, которые необходимо внести в клиентское приложение проверяющей стороны для создания ключа доступа, а также дается краткий обзор реализации сервера приложения проверяющей стороны . Подробнее об интеграции на стороне сервера см. в разделе Регистрация ключа доступа на стороне сервера .
- Добавьте зависимости к вашему приложению : добавьте необходимые библиотеки диспетчера учетных данных.
- Создание экземпляра диспетчера учетных данных : создание экземпляра диспетчера учетных данных.
- Получите параметры создания учетных данных с сервера приложений : с сервера приложений отправьте клиентскому приложению данные, необходимые для создания ключа доступа, такие как информация о приложении, пользователе, а также поле
challengeи другие поля. - Запросите ключ доступа : в своем приложении используйте данные, полученные от сервера приложений, для создания объекта
GetPublicKeyCredentialOptionи используйте этот объект для вызова методаcredentialManager.getCredential()для создания ключа доступа. - Обработка ответа на запрос создания ключа доступа : при получении учётных данных в клиентском приложении необходимо закодировать, сериализовать и отправить открытый ключ на сервер приложения. Также необходимо обрабатывать все исключения, которые могут возникнуть при создании ключа доступа.
- Проверьте и сохраните открытый ключ на сервере : выполните шаги на стороне сервера, чтобы проверить происхождение учетных данных, а затем сохраните открытый ключ.
- Уведомить пользователя : уведомить пользователя о том, что его ключ доступа создан.
1. Добавьте зависимости в свое приложение
Добавьте следующие зависимости в файл build.gradle вашего модуля приложения:
Котлин
dependencies { implementation("androidx.credentials:credentials:1.6.0-beta03") implementation("androidx.credentials:credentials-play-services-auth:1.6.0-beta03") }
Круто
dependencies { implementation "androidx.credentials:credentials:1.6.0-beta03" implementation "androidx.credentials:credentials-play-services-auth:1.6.0-beta03" }
2. Создать диспетчер учетных данных
Используйте контекст вашего приложения или деятельности для создания объекта CredentialManager .
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
3. Получите параметры создания учетных данных на сервере приложений.
Когда пользователь нажимает кнопку «Создать ключ доступа» или когда регистрируется новый пользователь, отправьте запрос из вашего приложения на сервер приложений, чтобы получить информацию, необходимую для начала процесса регистрации ключа доступа.
Используйте FIDO-совместимую библиотеку на сервере приложений, чтобы передавать клиентскому приложению информацию, необходимую для создания ключа доступа, например, информацию о пользователе, приложении и дополнительные параметры конфигурации. Подробнее см. в разделе Регистрация ключа доступа на стороне сервера .
В клиентском приложении декодируйте параметры создания открытого ключа, отправленные сервером приложения. Обычно они представлены в формате JSON. Подробнее о декодировании для веб-клиентов см. в разделе Кодирование и декодирование . Для клиентских приложений Android декодирование необходимо выполнять отдельно.
В следующем фрагменте показана структура параметров создания открытого ключа, отправленных сервером приложений:
{
"challenge": "<base64url-encoded challenge>",
"rp": {
"name": "<relying party name>",
"id": "<relying party host name>"
},
"user": {
"id": "<base64url-encoded user ID>",
"name": "<user name>",
"displayName": "<user display name>"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"attestation": "none",
"excludeCredentials": [
{
"id": "<base64url-encoded credential ID to exclude>",
"type": "public-key"
}
],
"authenticatorSelection": {
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
Ключевые поля в параметрах создания открытого ключа включают:
-
challenge: генерируемая сервером случайная строка, используемая для предотвращения атак повторного воспроизведения. -
rp: Подробная информация о приложении.-
rp.name: Имя приложения. -
rp.id: Домен или поддомен приложения.
-
-
user: Подробная информация о пользователе.-
id: уникальный идентификатор пользователя. Это значение не должно включать персональную информацию, например, адреса электронной почты или имена пользователей. Можно использовать случайное 16-байтовое значение. -
name: уникальный идентификатор учётной записи, который будет распознан пользователем, например, адрес электронной почты или имя пользователя. Он будет отображаться в селекторе учётных записей. При использовании имени пользователя используйте то же значение, что и при аутентификации по паролю. -
displayName: Необязательное, удобное для пользователя имя учетной записи, предназначенное для отображения в селекторе учетных записей.
-
authenticatorSelection: Подробная информация об устройстве, которое будет использоваться для аутентификации.-
authenticatorAttachment: указывает предпочитаемый аутентификатор . Возможны следующие значения: -platform: это значение используется для аутентификатора, встроенного в устройство пользователя, например, сканера отпечатков пальцев. -cross-platform: это значение используется для перемещаемых устройств, таких как ключи безопасности. Обычно не используется в контексте ключа доступа. - Unspecified (рекомендуется): если оставить это значение неуказанным, пользователи смогут гибко создавать ключи доступа на своих предпочитаемых устройствах. В большинстве случаев наилучшим вариантом будет оставить этот параметр неуказанным.-
requireResidentKey: Чтобы создать ключ доступа, задайте для этогоBooleanполя значениеtrue. -
residentKey: Чтобы создать ключ доступа, задайте значениеrequired. -
userVerification: используется для указания требований к проверке пользователя при регистрации ключа доступа. Возможны следующие значения: -preferred: используйте это значение, если удобство пользователя важнее защиты, например, в средах, где проверка пользователя вызывает больше проблем, чем безопасность. -required: используйте это значение, если требуется использование метода проверки пользователя, доступного на устройстве. -discouraged: используйте это значение, если использование метода проверки пользователя не рекомендуется.
Более подробную информацию оuserVerificationсм. в разделе «Подробное описание userVerification» .
-
-
excludeCredentials: Перечислить идентификаторы учетных данных в массиве , чтобы предотвратить создание дубликата ключа доступа, если он уже существует у того же поставщика учетных данных.
4. Запросить ключ доступа
После анализа параметров создания открытого ключа на стороне сервера создайте ключ доступа, заключив эти параметры в объект CreatePublicKeyCredentialRequest и вызвав createCredential() .
createPublicKeyCredentialRequest включает в себя следующее:
-
requestJson: параметры создания учетных данных, отправленные сервером приложений. -
preferImmediatelyAvailableCredentials: это необязательное логическое поле, которое определяет, следует ли использовать для выполнения запроса только локально доступные или синхронизированные с поставщиком учетных данных учетные данные вместо учетных данных из ключей безопасности или гибридных потоков ключей. Возможные варианты использования:-
false(по умолчанию): используйте это значение, если вызов диспетчера учетных данных был инициирован явным действием пользователя. -
true: используйте это значение, если диспетчер учетных данных вызывается случайно, например, при первом открытии приложения.
Если установлено значениеtrueи нет доступных учетных данных, диспетчер учетных данных не отобразит никакого пользовательского интерфейса, а запрос немедленно завершится ошибкой, возвращая NoCredentialException для запросов на получение иCreateCredentialNoCreateOptionExceptionдля запросов на создание.
-
-
origin: это поле автоматически задаётся для приложений Android. Для браузеров и аналогичных привилегированных приложений, которым необходимо задатьorigin, см. статью Выполнение вызовов Credential Manager от имени других сторон для привилегированных приложений . -
isConditional: это необязательное поле, по умолчанию имеющее значениеfalse. Если установлено значениеtrueи у пользователя нет ключа доступа, он автоматически создаётся при следующем входе в систему с сохранённым паролем. Ключ доступа хранится у поставщика учётных данных пользователя. Для функции условного создания требуется последняя версияandroidx.credentials.
Вызов функции createCredential() запускает встроенный пользовательский интерфейс нижней страницы Credential Manager, который предлагает пользователю использовать ключ доступа и выбрать поставщика учётных данных и учётную запись для хранения. Однако, если isConditional имеет значение true , пользовательский интерфейс нижней страницы не отображается, а ключ доступа создаётся автоматически.
5. Обработайте ответ
После проверки пользователя с помощью блокировки экрана устройства создается ключ доступа, который сохраняется у выбранного пользователем поставщика учетных данных.
Ответом после успешного вызова createCredential() является объект PublicKeyCredential .
PublicKeyCredential выглядит следующим образом:
{
"id": "<identifier>",
"type": "public-key",
"rawId": "<identifier>",
"response": {
"clientDataJSON": "<ArrayBuffer encoded object with the origin and signed challenge>",
"attestationObject": "<ArrayBuffer encoded object with the public key and other information.>"
},
"authenticatorAttachment": "platform"
}
В клиентском приложении сериализуйте объект и отправьте его на сервер приложения.
Добавьте код для обработки сбоев, как показано в следующем фрагменте:
fun handleFailure(e: CreateCredentialException) {
when (e) {
is CreatePublicKeyCredentialDomException -> {
// Handle the passkey DOM errors thrown according to the
// WebAuthn spec.
}
is CreateCredentialCancellationException -> {
// The user intentionally canceled the operation and chose not
// to register the credential.
}
is CreateCredentialInterruptedException -> {
// Retry-able error. Consider retrying the call.
}
is CreateCredentialProviderConfigurationException -> {
// Your app is missing the provider configuration dependency.
// Most likely, you're missing the
// "credentials-play-services-auth" module.
}
is CreateCredentialCustomException -> {
// You have encountered an error from a 3rd-party SDK. If you
// make 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.name}")
}
}
6. Проверьте и сохраните открытый ключ на сервере приложения.
На сервере приложений необходимо проверить учетные данные открытого ключа, а затем сохранить открытый ключ .
Чтобы проверить происхождение учётных данных открытого ключа, сравните его со списком разрешённых приложений. Если происхождение ключа неизвестно, отклоните его.
Чтобы получить отпечаток SHA 256 приложения:
Распечатайте сертификат подписи вашего приложения, выполнив следующую команду в терминале:
keytool -list -keystore <path-to-apk-signing-keystore>В ответе укажите отпечаток SHA 256 сертификата подписи, указанный как
Certificate fingerprints block:SHA256.Закодируйте отпечаток SHA256 с помощью кодировки base64url. Этот пример на Python демонстрирует, как правильно закодировать отпечаток:
import binascii import base64 fingerprint = '<SHA256 finerprint>' # your app's SHA256 fingerprint print(base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))Добавьте
android:apk-key-hash: к началу вывода из предыдущего шага, чтобы получить что-то похожее на следующее:android:apk-key-hash:<encoded SHA 256 fingerprint>Результат должен совпадать с разрешённым источником на вашем сервере приложений. Если у вас несколько сертификатов подписи, например, для отладки и выпуска, или сертификаты для нескольких приложений, повторите процесс и примите все источники как действительные на сервере приложений.
7. Уведомить пользователя
После успешного создания ключа доступа сообщите пользователям о нём и о том, что они могут управлять своими ключами доступа из приложения поставщика учётных данных или в настройках приложения . Уведомляйте пользователей с помощью настраиваемого диалогового окна, уведомления или панели уведомлений. Поскольку непредвиденное создание ключа доступа злоумышленником требует немедленного оповещения системы безопасности, рассмотрите возможность дополнения этих методов внутри приложения внешними средствами связи, например, электронной почтой.
Улучшить пользовательский опыт
Чтобы улучшить пользовательский опыт при внедрении регистрации с помощью Credential Manager, рассмотрите возможность добавления функций восстановления учетных данных и отключения диалоговых окон автозаполнения.
Добавить функцию восстановления учетных данных на новом устройстве
Чтобы пользователи могли беспрепятственно входить в свои учётные записи на новом устройстве, реализуйте функцию восстановления учётных данных . Добавление учётных данных для восстановления с помощью BackupAgent позволяет пользователям сразу же войти в систему при открытии восстановленного приложения на новом устройстве, позволяя им сразу же начать использовать приложение.
Отключить автозаполнение полей учетных данных (необязательно)
Для экранов приложений, где пользователи будут использовать нижний интерфейс Credential Manager для аутентификации, добавьте атрибут isCredential к полям имени пользователя и пароля. Это предотвратит перекрытие диалоговых окон автозаполнения ( FillDialog и SaveDialog ) с нижним интерфейсом Credential Manager.
Атрибут isCredential поддерживается в Android 14 и выше.
В следующем примере показано, как можно добавить атрибут isCredential к соответствующим полям имени пользователя и пароля в соответствующих представлениях вашего приложения:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isCredential="true" />
Следующие шаги
- Войти с помощью паролей
- Управление ключами доступа
- Понять потоки взаимодействия пользователя с ключом доступа