Credential Manager — это API Jetpack, который поддерживает несколько методов входа, таких как имя пользователя и пароль, ключи доступа и интегрированные решения для входа (например, вход через Google) в одном API, что упрощает интеграцию для разработчиков.
Кроме того, Credential Manager унифицирует интерфейс входа для пользователей при использовании различных методов аутентификации, делая вход в приложения более понятным и простым, независимо от выбранного ими метода.
На этой странице объясняется концепция ключей доступа и шаги по внедрению поддержки на стороне клиента для решений аутентификации, включая ключи доступа, с использованием API Credential Manager. Также есть отдельная страница FAQ , которая дает ответы на более подробные, конкретные вопросы.
О ключах доступа
Пароли — более безопасная и простая замена паролям. С помощью паролей пользователи могут входить в приложения и на веб-сайты с помощью биометрического датчика (например, отпечатка пальца или распознавания лица), PIN-кода или шаблона. Это обеспечивает бесперебойный процесс входа, освобождая ваших пользователей от необходимости запоминать имена пользователей или пароли.
Пароли основаны на WebAuthn (веб-аутентификация), стандарте, совместно разработанном FIDO Alliance и World Wide Web Consortium (W3C). WebAuthn использует криптографию с открытым ключом для аутентификации пользователя. Веб-сайт или приложение, в которое входит пользователь, может видеть и хранить открытый ключ, но никогда закрытый ключ. Закрытый ключ хранится в секрете и в безопасности. А поскольку ключ уникален и привязан к веб-сайту или приложению, пароли не поддаются фишингу, что обеспечивает дополнительную безопасность.
Диспетчер учетных данных позволяет пользователям создавать пароли и хранить их в Google Password Manager .
Ознакомьтесь с разделом Аутентификация пользователей с помощью паролей, чтобы узнать, как реализовать бесперебойные потоки аутентификации с помощью паролей с помощью Credential Manager.
Предпосылки
Чтобы использовать Credential Manager, выполните действия, описанные в этом разделе.
Используйте последнюю версию платформы
Credential Manager поддерживается на Android 4.4 (API уровня 19) и выше.
Добавьте зависимости в ваше приложение
Добавьте следующие зависимости в скрипт сборки модуля вашего приложения:
implementation(libs.androidx.credentials)
// optional - needed for credentials support from play services, for devices running
// Android 13 and below.
implementation(libs.androidx.credentials.play.services.auth)
Узнайте больше о том, как уменьшить, скрыть и оптимизировать ваше приложение .
Добавить поддержку ссылок на цифровые активы
Чтобы включить поддержку ключа доступа для вашего приложения Android, свяжите свое приложение с веб-сайтом, которым владеет ваше приложение. Вы можете объявить эту связь, выполнив следующие шаги:
Создайте файл Digital Asset Links JSON. Например, чтобы объявить, что веб-сайт
https://signin.example.com
и приложение Android с именем пакетаcom.example
могут совместно использовать учетные данные для входа, создайте файл с именемassetlinks.json
со следующим содержимым:[ { "relation" : [ "delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds" ], "target" : { "namespace" : "android_app", "package_name" : "com.example.android", "sha256_cert_fingerprints" : [ SHA_HEX_VALUE ] } } ]
Поле
relation
— это массив из одной или нескольких строк, описывающих объявляемую связь. Чтобы объявить, что приложения и сайты совместно используют учетные данные для входа, укажите отношения какdelegate_permission/handle_all_urls
иdelegate_permission/common.get_login_creds
.Поле
target
— это объект, который определяет актив, к которому применяется декларация. Следующие поля идентифицируют веб-сайт:namespace
web
site
URL-адрес веб-сайта в формате
https:// domain [: optional_port ]
; например,https://www.example.com
.domain должен быть полностью определен, а optional_port порт необходимо опустить при использовании порта 443 для HTTPS.
Целью
site
может быть только корневой домен: вы не можете ограничить ассоциацию приложения определенным подкаталогом. Не включайте путь в URL, например, завершающий слеш.Субдомены не считаются совпадающими: то есть, если вы указываете domain как
www.example.com
, доменwww.counter.example.com
не будет связан с вашим приложением.Следующие поля идентифицируют приложение Android:
namespace
android_app
package_name
Имя пакета, объявленное в манифесте приложения. Например, com.example.android
sha256_cert_fingerprints
Отпечатки пальцев SHA256 сертификата подписи вашего приложения. Разместите файл JSON ссылки на цифровые активы в следующем месте на домене входа:
https://domain[:optional_port]/.well-known/assetlinks.json
Например, если ваш домен входа —
signin.example.com
, разместите файл JSON по адресуhttps://signin.example.com/.well-known/assetlinks.json
.Тип MIME для файла ссылки на цифровые активы должен быть JSON. Убедитесь, что сервер отправляет заголовок
Content-Type: application/json
в ответе.Убедитесь, что ваш хост разрешает Google извлекать ваш файл Digital Asset Link. Если у вас есть файл
robots.txt
, он должен разрешать агенту Googlebot извлекать/.well-known/assetlinks.json
. Большинство сайтов могут разрешить любому автоматизированному агенту извлекать файлы по пути/.well-known/
, чтобы другие службы могли получить доступ к метаданным в этих файлах:User-agent: * Allow: /.well-known/
Добавьте следующую строку в файл манифеста в разделе
<application>
:<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
Если вы используете вход с паролем через Credential Manager, выполните этот шаг для настройки привязки цифровых активов в манифесте. Этот шаг не требуется, если вы используете только пароли.
Объявите ассоциацию в приложении Android. Добавьте объект, который указывает файлы
assetlinks.json
для загрузки. Вы должны экранировать любые апострофы и кавычки, которые вы используете в строке. Например:<string name="asset_statements" translatable="false"> [{ \"include\": \"https://signin.example.com/.well-known/assetlinks.json\" }] </string>
> GET /.well-known/assetlinks.json HTTP/1.1 > User-Agent: curl/7.35.0 > Host: signin.example.com < HTTP/1.1 200 OK < Content-Type: application/json
Настройте диспетчер учетных данных
Чтобы настроить и инициализировать объект CredentialManager
, добавьте логику, подобную следующей:
Котлин
// Use your app or activity context to instantiate a client instance of // CredentialManager. val credentialManager = CredentialManager.create(context)
Ява
// Use your app or activity context to instantiate a client instance of // CredentialManager. CredentialManager credentialManager = CredentialManager.create(context)
Укажите поля учетных данных
На Android 14 и выше атрибут isCredential
может использоваться для указания полей учетных данных, таких как поля имени пользователя или пароля. Этот атрибут указывает, что это представление является полем учетных данных, которое предназначено для работы с Credential Manager и сторонними поставщиками учетных данных, помогая службам автозаполнения предоставлять лучшие предложения по автозаполнению. Когда приложение использует API Credential Manager, отображается нижний лист Credential Manager с доступными учетными данными, и больше нет необходимости показывать диалоговое окно автозаполнения для имени пользователя или пароля. Аналогичным образом нет необходимости показывать диалоговое окно сохранения автозаполнения для паролей, поскольку приложение запросит API Credential Manager для сохранения учетных данных.
Чтобы использовать атрибут isCredential
, добавьте его в соответствующие представления:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isCredential="true"
...
/>
Войдите в систему как пользователь
Чтобы получить все параметры ключа доступа и пароля, связанные с учетной записью пользователя, выполните следующие действия:
Инициализируйте параметры аутентификации по паролю и ключу доступа:
Котлин
// Retrieves the user's saved password for your app from their // password provider. val getPasswordOption = GetPasswordOption() // Get passkey from the user's public key credential provider. val getPublicKeyCredentialOption = GetPublicKeyCredentialOption( requestJson = requestJson )
Ява
// Retrieves the user's saved password for your app from their // password provider. GetPasswordOption getPasswordOption = new GetPasswordOption(); // Get passkey from the user's public key credential provider. GetPublicKeyCredentialOption getPublicKeyCredentialOption = new GetPublicKeyCredentialOption(requestJson);
Используйте параметры, полученные на предыдущем шаге, для создания запроса на вход.
Котлин
val getCredRequest = GetCredentialRequest( listOf(getPasswordOption, getPublicKeyCredentialOption) )
Ява
GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder() .addCredentialOption(getPasswordOption) .addCredentialOption(getPublicKeyCredentialOption) .build();
Запустите процесс входа:
Котлин
coroutineScope.launch { try { val result = credentialManager.getCredential( // Use an activity-based context to avoid undefined system UI // launching behavior. context = activityContext, request = getCredRequest ) handleSignIn(result) } catch (e : GetCredentialException) { handleFailure(e) } } 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") } } }
Ява
credentialManager.getCredentialAsync( // Use activity based context to avoid undefined // system UI launching behavior activity, getCredRequest, cancellationSignal, <executor>, new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() { @Override public void onResult(GetCredentialResponse result) { handleSignIn(result); } @Override public void onError(GetCredentialException e) { handleFailure(e); } } ); public void handleSignIn(GetCredentialResponse result) { // Handle the successfully returned credential. Credential credential = result.getCredential(); if (credential instanceof PublicKeyCredential) { String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson(); // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate } else if (credential instanceof PasswordCredential) { String username = ((PasswordCredential) credential).getId(); String password = ((PasswordCredential) credential).getPassword(); // Use id and password to send to your server to validate and authenticate } else if (credential instanceof CustomCredential) { if (ExampleCustomCredential.TYPE.equals(credential.getType())) { try { ExampleCustomCredential customCred = ExampleCustomCredential.createFrom(customCredential.getData()); // Extract the required credentials and complete the // authentication as per the federated sign in or any external // sign in library flow } catch (ExampleCustomCredential.ExampleCustomCredentialParsingException e) { // 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"); } }
В следующем примере показано, как отформатировать запрос JSON при получении ключа доступа:
{
"challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
"allowCredentials": [],
"timeout": 1800000,
"userVerification": "required",
"rpId": "credential-manager-app-test.glitch.me"
}
В следующем примере показано, как может выглядеть ответ JSON после получения учетных данных открытого ключа:
{
"id": "KEDetxZcUfinhVi6Za5nZQ",
"type": "public-key",
"rawId": "KEDetxZcUfinhVi6Za5nZQ",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
"authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
"signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
"userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
}
}
Обрабатывать исключения, когда учетные данные недоступны
В некоторых случаях у пользователя может не быть никаких учетных данных, или пользователь может не дать согласия на использование доступных учетных данных. Если вызывается getCredential()
и учетные данные не найдены, возвращается NoCredentialException
. Если это происходит, ваш код должен обрабатывать экземпляры NoCredentialException
.
Котлин
try {
val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
Log.e("CredentialManager", "No credential available", e)
}
Ява
try {
Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
Log.e("CredentialManager", "No credential available", e);
}
В Android 14 и выше можно сократить задержку при отображении селектора учетных записей, используя метод prepareGetCredential()
перед вызовом getCredential()
.
Котлин
val response = credentialManager.prepareGetCredential(
GetCredentialRequest(
listOf(
<getPublicKeyCredentialOption>,
<getPasswordOption>
)
)
}
Ява
GetCredentialResponse response = credentialManager.prepareGetCredential(
new GetCredentialRequest(
Arrays.asList(
new PublicKeyCredentialOption(),
new PasswordOption()
)
)
);
Метод prepareGetCredential()
не вызывает элементы пользовательского интерфейса. Он только помогает вам выполнить подготовительную работу, чтобы вы могли позже запустить оставшуюся операцию получения учетных данных (которая включает в себя пользовательские интерфейсы) через API getCredential()
.
Кэшированные данные возвращаются в объекте PrepareGetCredentialResponse
. Если есть существующие учетные данные, результаты будут кэшированы, и вы сможете позже запустить оставшийся API getCredential()
, чтобы вызвать селектор учетных записей с кэшированными данными.
Регистрационные потоки
Вы можете зарегистрировать пользователя для аутентификации с использованием ключа доступа или пароля .
Создать пароль
Чтобы предоставить пользователям возможность зарегистрировать ключ доступа и использовать его для повторной аутентификации, зарегистрируйте учетные данные пользователя с помощью объекта CreatePublicKeyCredentialRequest
.
Котлин
fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) { val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest( // Contains the request in JSON format. Uses the standard WebAuthn // web JSON spec. requestJson = requestJson, // Defines whether you prefer to use only immediately available // credentials, not hybrid credentials, to fulfill this request. // This value is false by default. preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials, ) // Execute CreateCredentialRequest asynchronously to register credentials // for a user account. Handle success and failure cases with the result and // exceptions, respectively. coroutineScope.launch { try { val result = credentialManager.createCredential( // Use an activity-based context to avoid undefined system // UI launching behavior context = activityContext, request = createPublicKeyCredentialRequest, ) handlePasskeyRegistrationResult(result) } catch (e : CreateCredentialException){ handleFailure(e) } } } fun handleFailure(e: CreateCredentialException) { when (e) { is CreatePublicKeyCredentialDomException -> { // Handle the passkey DOM errors thrown according to the // WebAuthn spec. handlePasskeyError(e.domError) } 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 CreateCredentialUnknownException -> ... 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}") } }
Ява
public void createPasskey(String requestJson, boolean preferImmediatelyAvailableCredentials) { CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest = // `requestJson` contains the request in JSON format. Uses the standard // WebAuthn web JSON spec. // `preferImmediatelyAvailableCredentials` defines whether you prefer // to only use immediately available credentials, not hybrid credentials, // to fulfill this request. This value is false by default. new CreatePublicKeyCredentialRequest( requestJson, preferImmediatelyAvailableCredentials); // Execute CreateCredentialRequest asynchronously to register credentials // for a user account. Handle success and failure cases with the result and // exceptions, respectively. credentialManager.createCredentialAsync( // Use an activity-based context to avoid undefined system // UI launching behavior requireActivity(), createPublicKeyCredentialRequest, cancellationSignal, executor, new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() { @Override public void onResult(CreateCredentialResponse result) { handleSuccessfulCreatePasskeyResult(result); } @Override public void onError(CreateCredentialException e) { if (e instanceof CreatePublicKeyCredentialDomException) { // Handle the passkey DOM errors thrown according to the // WebAuthn spec. handlePasskeyError(((CreatePublicKeyCredentialDomException)e).getDomError()); } else if (e instanceof CreateCredentialCancellationException) { // The user intentionally canceled the operation and chose not // to register the credential. } else if (e instanceof CreateCredentialInterruptedException) { // Retry-able error. Consider retrying the call. } else if (e instanceof CreateCredentialProviderConfigurationException) { // Your app is missing the provider configuration dependency. // Most likely, you're missing the // "credentials-play-services-auth" module. } else if (e instanceof CreateCredentialUnknownException) { } else if (e instanceof 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.getClass().getName()); } } } ); }
Форматируйте запрос JSON
После создания ключа доступа необходимо связать его с учетной записью пользователя и сохранить открытый ключ ключа доступа на сервере. В следующем примере кода показано, как форматировать запрос JSON при создании ключа доступа.
Эта запись в блоге о внедрении бесшовной аутентификации в ваши приложения показывает, как форматировать запрос JSON при создании паролей и аутентификации с использованием паролей. Она также объясняет, почему пароли не являются эффективным решением для аутентификации, как использовать существующие биометрические учетные данные, как связать ваше приложение с веб-сайтом, владельцем которого вы являетесь, как создавать пароли и как аутентифицироваться с использованием паролей.
{
"challenge": "abc123",
"rp": {
"name": "Credential Manager example",
"id": "credential-manager-test.example.com"
},
"user": {
"id": "def456",
"name": "helloandroid@gmail.com",
"displayName": "helloandroid@gmail.com"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [
{"id": "ghi789", "type": "public-key"},
{"id": "jkl012", "type": "public-key"}
],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
Установите значения для authenticatorAttachment
Параметр authenticatorAttachment
можно задать только во время создания учетных данных. Можно указать platform
, cross-platform
или не указывать значение. В большинстве случаев рекомендуется не указывать значение.
-
platform
: чтобы зарегистрировать текущее устройство пользователя или предложить пользователю с паролем перейти на ключи доступа после входа в систему, установитеauthenticatorAttachment
наplatform
. -
cross-platform
: это значение обычно используется при регистрации многофакторных учетных данных и не применяется в контексте ключа доступа. - Нет значения : чтобы предоставить пользователям возможность создавать пароли на своих предпочитаемых устройствах (например, в настройках учетной записи), параметр
authenticatorAttachment
не следует указывать, когда пользователь выбирает добавление пароля. В большинстве случаев лучшим вариантом будет оставить параметр неуказанным.
Предотвратить создание дубликатов паролей
Перечислите идентификаторы учетных данных в необязательном массиве excludeCredentials
, чтобы предотвратить создание нового ключа доступа, если он уже существует у того же поставщика ключа доступа.
Обработка ответа JSON
Следующий фрагмент кода показывает пример ответа JSON для создания учетных данных открытого ключа. Узнайте больше о том, как обрабатывать возвращенные учетные данные открытого ключа .
{
"id": "KEDetxZcUfinhVi6Za5nZQ",
"type": "public-key",
"rawId": "KEDetxZcUfinhVi6Za5nZQ",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
}
}
Проверка источника на основе клиентских данных JSON
origin
представляет собой приложение или веб-сайт, с которого поступает запрос, и используется ключами доступа для защиты от фишинговых атак. Сервер вашего приложения должен проверить источник клиентских данных по списку разрешенных приложений и веб-сайтов. Если сервер получает запрос от приложения или веб-сайта из неизвестного источника, запрос должен быть отклонен.
В случае с веб-сайтом origin
отражает тот же сайт , на котором был выполнен вход с использованием учетных данных. Например, если URL-адрес https://www.example.com:8443/store?category=shoes#athletic
, origin
будет https://www.example.com:8443
.
Для приложений Android пользовательский агент автоматически устанавливает origin
на подпись вызывающего приложения. Эта подпись должна быть проверена на соответствие на вашем сервере для проверки вызывающей стороны API ключа доступа. Android origin
— это URI, полученный из хэша SHA-256 сертификата подписи APK, например:
android:apk-key-hash:<sha256_hash-of-apk-signing-cert>
Хэши SHA-256 сертификатов подписи из хранилища ключей можно найти, выполнив следующую команду терминала:
keytool -list -keystore <path-to-apk-signing-keystore>
Хэши SHA-256 находятся в шестнадцатеричном формате, разделенном двоеточием ( 91:F7:CB:F9:D6:81…
), а origin
значения Android закодированы в base64url. Этот пример Python демонстрирует, как преобразовать формат хеша в совместимый шестнадцатеричный формат, разделенный двоеточием:
import binascii
import base64
fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5'
print("android:apk-key-hash:" + base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))
Замените значение fingerprint
на свое собственное значение. Вот пример результата:
android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU
Затем вы можете сопоставить эту строку с разрешенным источником на вашем сервере. Если у вас есть несколько сертификатов подписи, например, сертификаты для отладки и выпуска или несколько приложений, повторите процесс и примите все эти источники как действительные на сервере.
Сохранить пароль пользователя
Если пользователь предоставляет имя пользователя и пароль для потока аутентификации в вашем приложении, вы можете зарегистрировать учетные данные пользователя, которые могут использоваться для аутентификации пользователя. Для этого создайте объект CreatePasswordRequest
:
Котлин
fun registerPassword(username: String, password: String) { // Initialize a CreatePasswordRequest object. val createPasswordRequest = CreatePasswordRequest(id = username, password = password) // Create credential and handle result. coroutineScope.launch { try { val result = credentialManager.createCredential( // Use an activity based context to avoid undefined // system UI launching behavior. activityContext, createPasswordRequest ) handleRegisterPasswordResult(result) } catch (e: CreateCredentialException) { handleFailure(e) } } }
Ява
void registerPassword(String username, String password) { // Initialize a CreatePasswordRequest object. CreatePasswordRequest createPasswordRequest = new CreatePasswordRequest(username, password); // Register the username and password. credentialManager.createCredentialAsync( // Use an activity-based context to avoid undefined // system UI launching behavior requireActivity(), createPasswordRequest, cancellationSignal, executor, new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() { @Override public void onResult(CreateCredentialResponse result) { handleResult(result); } @Override public void onError(CreateCredentialException e) { handleFailure(e); } } ); }
Поддержка восстановления учетных данных
Если у пользователя больше нет доступа к устройству, на котором он сохранил свои учетные данные, ему может потребоваться восстановление из безопасной онлайн-резервной копии. Чтобы узнать больше о том, как поддержать этот процесс восстановления учетных данных, прочитайте раздел под названием «Восстановление доступа или добавление новых устройств» в этой записи блога: Безопасность паролей в диспетчере паролей Google .
Автоматически создавать пароли для пользователей
Если у пользователя нет ключа доступа, вы можете автоматически создать его от его имени при следующем входе в систему с использованием пароля, сохраненного в его менеджере паролей. Сделайте это, установив поле isConditionalCreateRequest
при запросе публичных учетных данных:
CreatePublicKeyCredentialRequest(
// other parameters
isConditionalCreateRequest: Boolean = true
)
Когда пользователь входит в систему, автоматически создается и сохраняется в выбранном пользователем менеджере паролей ключ доступа. Если используется Google Password Manager, пользователь должен использовать (используя Credential Manager или автозаполнение) пароль, сохраненный в менеджере паролей. Пользователь получает уведомление при создании этого ключа доступа и может перейти в менеджер паролей, чтобы управлять им.
Для этой функции требуется версия 1.6.0-alpha01 или выше.
Добавить поддержку инструментов управления паролями с конечными точками ключа доступа и известными URL-адресами
Для бесшовной интеграции и будущей совместимости с инструментами управления паролями и учетными данными мы рекомендуем добавить поддержку известных URL-адресов конечных точек ключей доступа. Это открытый протокол для согласованных сторон, чтобы официально объявить о своей поддержке ключей доступа и предоставить прямые ссылки для регистрации и управления ключами доступа.
- Для проверяющей стороны по адресу
https://example.com
, у которой есть веб-сайт, а также приложения для Android и iOS, известным URL-адресом будетhttps://example.com/.well-known/passkey-endpoints
. При запросе URL-адреса ответ должен использовать следующую схему
{ "enroll": "https://example.com/account/manage/passkeys/create" "manage": "https://example.com/account/manage/passkeys" }
Чтобы эта ссылка открывалась непосредственно в вашем приложении, а не в Интернете, используйте ссылки приложений Android .
Более подробную информацию можно найти в известном объяснении URL-адресов конечных точек ключа доступа на GitHub.
Помогите пользователям управлять своими ключами доступа, указав, какой поставщик их создал
Одной из проблем, с которой сталкиваются пользователи при управлении несколькими ключами доступа, связанными с данным приложением, является определение правильного ключа доступа для редактирования или удаления. Чтобы помочь решить эту проблему, рекомендуется, чтобы приложения и веб-сайты включали дополнительную информацию, такую как поставщик, создавший учетные данные, дату создания и дату последнего использования в списке ключей доступа на экране настроек вашего приложения. Информация о поставщике получается путем изучения AAGUID , связанного с соответствующим ключом доступа. AAGUID можно найти как часть данных аутентификатора ключа доступа.
Например, если пользователь создает пароль на устройстве Android с помощью Google Password Manager, RP получает AAGUID, который выглядит примерно так: "ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4". Доверяющая сторона может аннотировать пароль в списке паролей, чтобы указать, что он был создан с помощью Google Password Manager.
Чтобы сопоставить AAGUID с поставщиком ключа доступа, RP могут использовать репозиторий AAGUID, созданный сообществом . Найдите AAGUID в списке, чтобы найти имя и значок поставщика ключа доступа.
Узнайте больше об интеграции AAGUID .
Устранение распространенных ошибок
Обратитесь к руководству по устранению неполадок диспетчера учетных данных для получения информации о распространенных кодах ошибок, их описаниях и причинах.
Дополнительные ресурсы
Чтобы узнать больше об API Credential Manager и ключах доступа, ознакомьтесь со следующими ресурсами:
- Руководство по использованию Passkeys
- Видео: Как уменьшить зависимость от паролей в приложениях Android с помощью поддержки паролей
- Codelab: узнайте, как упростить процесс аутентификации с помощью API Credential Manager в вашем приложении Android
- Пример приложения: CredentialManager