В этом руководстве рассматривается реализация аутентификации с помощью паролей. Прежде чем пользователи смогут входить в систему с помощью паролей, необходимо также выполнить инструкции в разделе «Создание паролей» .
Для аутентификации с помощью пароля необходимо сначала получить параметры, необходимые для получения открытого ключа, с вашего сервера приложений , а затем вызвать API диспетчера учетных данных для получения открытого ключа. После этого необходимо соответствующим образом обработать ответ на запрос входа в систему.
Обзор
В этом руководстве рассматриваются изменения, необходимые в вашем клиентском приложении для авторизации пользователя с помощью пароля, и дается краткий обзор реализации на стороне сервера. Для получения дополнительной информации об интеграции на стороне сервера см. раздел «Аутентификация с помощью пароля на стороне сервера» .
Чтобы получить доступ ко всем параметрам пароля и ключам доступа, связанным с учетной записью пользователя, выполните следующие действия:
- Получение параметров запроса учетных данных с сервера : отправьте запрос из вашего приложения на сервер аутентификации, чтобы начать процесс входа в систему с помощью пароля. С сервера отправьте параметры, необходимые для получения учетных данных открытого ключа, а также уникальный запрос.
- Создайте объект, необходимый для получения учетных данных открытого ключа : оберните параметры, отправленные сервером, в объект
GetPublicKeyCredentialOption - ( необязательно) Подготовка метода getCredential : В Android 14 и выше можно уменьшить задержку, отобразив селектор учетной записи с помощью метода
prepareGetCredential()перед вызовом методаgetCredential(). - Запуск процесса авторизации : вызовите метод
getCredential()для авторизации пользователя. - Обработка ответа : обработайте каждый из возможных ответов, содержащих учетные данные.
- Обработка исключений : Убедитесь, что вы надлежащим образом обрабатываете исключения.
Получите параметры запроса учетных данных с сервера.
Запросите у сервера параметры, необходимые для получения учетных данных открытого ключа, а также challenge , который уникален для каждой попытки входа в систему. Подробнее о реализации на стороне сервера см. в разделах «Создание запроса на ввод данных» и «Создание параметров запроса учетных данных» .
Варианты выглядят примерно так:
{
"challenge": "<your app challenge>",
"allowCredentials": [],
"rpId": "<your app server domain>"
}
Чтобы узнать больше об этих полях, ознакомьтесь с записью в блоге о входе в систему с помощью пароля .
Создайте объект, необходимый для получения учетных данных открытого ключа.
В вашем приложении используйте параметры для создания объекта 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
)
Дополнительно: Уменьшить задержку при входе в систему.
На 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
)
)
)
}
Запустите процесс авторизации.
Вызовите метод getCredential() , чтобы показать пользователю окно выбора учетной записи. Используйте следующий фрагмент кода в качестве примера запуска процесса входа в систему:
// Use an activity-based context to avoid undefined system UI
// launching behavior.
val context = MutableContextWrapper(activityContext)
coroutineScope {
try {
result = credentialManager.getCredential(
// Use MutableContextWrapper to avoid memory leak during configuration changes
context = context,
request = credentialRequest
)
handleSignIn(result)
} catch (e: GetCredentialException) {
// Handle failure
}
}
Обработайте ответ
Обработайте ответ, который может содержать один из различных типов объектов учетных данных.
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>"
}
}
На сервере необходимо проверить учетные данные. Для получения дополнительной информации см. раздел «Проверка и вход пользователя» .
Обработка исключений
Необходимо обрабатывать все исключения подкласса GetCredentialException . Чтобы узнать, как обрабатывать каждое исключение, см. руководство по устранению неполадок .
coroutineScope {
try {
result = credentialManager.getCredential(
context = activityContext,
request = credentialRequest
)
} catch (e: GetCredentialException) {
Log.e("CredentialManager", "No credential available", e)
}
}