Это руководство продолжает тему внедрения использования паролей для аутентификации. Прежде чем пользователи смогут входить в систему с помощью паролей, необходимо также выполнить инструкции из раздела Создание паролей .
Для аутентификации с помощью ключа доступа необходимо сначала получить параметры, необходимые для получения открытого ключа с сервера приложений , а затем вызвать API диспетчера учётных данных для получения открытого ключа. После этого обработайте ответ на запрос входа соответствующим образом.
Обзор
В этом руководстве рассматриваются изменения, которые необходимо внести в клиентское приложение для входа пользователя с помощью ключа доступа, а также дается краткий обзор реализации приложения на стороне сервера. Подробнее об интеграции на стороне сервера см. в разделе Аутентификация с помощью ключа доступа на стороне сервера .
Чтобы получить все параметры ключа доступа и пароля, связанные с учетной записью пользователя, выполните следующие действия:
- Получите параметры запроса учётных данных с сервера : отправьте запрос из вашего приложения на сервер аутентификации, чтобы начать процесс входа с ключом доступа. Отправьте с сервера параметры, необходимые для получения учётных данных открытого ключа, а также уникальный запрос.
- Создайте объект, необходимый для получения учетных данных открытого ключа : оберните параметры, отправленные сервером, в объект
GetPublicKeyCredentialOption - ( необязательно) Подготовка getCredential : в Android 14 и более поздних версиях можно сократить задержку, отобразив селектор учетных записей с помощью метода
prepareGetCredential()перед вызовомgetCredential(). - Запустите процесс входа : вызовите метод
getCredential()для входа пользователя. - Обработка ответа : обработка каждого из возможных ответов на учетные данные.
- Обработка исключений : убедитесь, что вы обрабатываете исключения правильно.
1. Получите параметры запроса учетных данных с сервера.
Запросите у сервера параметры, необходимые для получения учётных данных открытого ключа, а также challenge , уникальный для каждой попытки входа. Подробнее о реализации на стороне сервера см. в разделах Создание запроса и Создание параметров запроса на проверку подлинности .
Параметры выглядят примерно так:
{
"challenge": "<your app challenge>",
"allowCredentials": [],
"rpId": "<your app server domain>"
}
Дополнительную информацию о полях см. в записи в блоге о входе с использованием ключа доступа .
2. Создайте объект, необходимый для получения учетных данных открытого ключа.
В вашем приложении используйте параметры для создания объекта GetPublicKeyCredentialOption . В следующем примере requestJson представляет параметры, отправленные сервером.
// Get password logins from the credential provider on the user's device.
val getPasswordOption = GetPasswordOption()
// Get passkeys from the credential provider on the user's device.
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
requestJson = requestJson
)
Затем заключите GetPublicKeyCredentialOption в объект GetCredentialRequest .
val credentialRequest = GetCredentialRequest(
// Include all the sign-in options that your app supports.
listOf(getPasswordOption, getPublicKeyCredentialOption),
// Defines whether you prefer to use only immediately available
// credentials or hybrid credentials.
preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials
)
3. Дополнительно: сократите задержку входа
На устройствах с Android 14 и выше можно уменьшить задержку при отображении селектора учетных записей, используя метод prepareGetCredential() перед вызовом getCredential() .
Метод prepareGetCredential() возвращает кэшированный объект PrepareGetCredentialResponse . Это позволяет методу getCredential() на следующем шаге открыть селектор учётных записей с кэшированными данными.
coroutineScope {
val response = credentialManager.prepareGetCredential(
GetCredentialRequest(
listOf(
// Include all the sign-in options that your app supports
getPublicKeyCredentialOption,
getPasswordOption
)
)
)
}
4. Запустите процесс входа в систему.
Вызовите метод getCredential() чтобы показать пользователю выбор учётной записи. Используйте следующий фрагмент кода в качестве примера запуска процесса входа:
coroutineScope {
try {
result = credentialManager.getCredential(
// Use an activity-based context to avoid undefined system UI
// launching behavior.
context = activityContext,
request = credentialRequest
)
handleSignIn(result)
} catch (e: GetCredentialException) {
// Handle failure
}
}
5. Обработайте ответ
Обработайте ответ, который может содержать один из различных типов объектов учетных данных.
fun handleSignIn(result: GetCredentialResponse) {
// Handle the successfully returned credential.
val credential = result.credential
when (credential) {
is PublicKeyCredential -> {
val responseJson = credential.authenticationResponseJson
// Share responseJson i.e. a GetCredentialResponse on your server to
// validate and authenticate
}
is PasswordCredential -> {
val username = credential.id
val password = credential.password
// Use id and password to send to your server to validate
// and authenticate
}
is CustomCredential -> {
// If you are also using any external sign-in libraries, parse them
// here with the utility functions provided.
if (credential.type == ExampleCustomCredential.TYPE) {
try {
val ExampleCustomCredential =
ExampleCustomCredential.createFrom(credential.data)
// Extract the required credentials and complete the authentication as per
// the federated sign in or any external sign in library flow
} catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
// Unlikely to happen. If it does, you likely need to update the dependency
// version of your external sign-in library.
Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
}
} else {
// Catch any unrecognized custom credential type here.
Log.e(TAG, "Unexpected type of credential")
}
}
else -> {
// Catch any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential")
}
}
}
PublicKeyCredential , возвращаемый в результате аутентификации, по сути представляет собой подписанное утверждение, структурированное следующим образом:
{
"id": "<credential ID>",
"type": "public-key",
"rawId": "<raw credential ID>",
"response": {
"clientDataJSON": "<signed client data containing challenge>",
"authenticatorData": "<authenticator metadata>",
"signature": "<digital signature to be verified>",
"userHandle": "<user ID from credential registration>"
}
}
На сервере необходимо подтвердить учётные данные. Подробнее см. в разделе «Подтверждение и вход пользователя» .
6. Обработка исключений
Необходимо обработать все исключения подкласса GetCredentialException . Чтобы узнать, как обрабатывать каждое исключение, см. руководство по устранению неполадок .
coroutineScope {
try {
result = credentialManager.getCredential(
context = activityContext,
request = credentialRequest
)
} catch (e: GetCredentialException) {
Log.e("CredentialManager", "No credential available", e)
}
}