Редакция 2025, 4 квартал: Узнайте, как упростить процесс аутентификации с помощью API Credential Manager в вашем Android-приложении.

1. Прежде чем начать

Традиционные решения для аутентификации создают ряд проблем, связанных с безопасностью и удобством использования.

Пароли широко используются, но...

  • Легко забывается
  • Пользователям необходимы знания для создания надежных паролей.
  • Злоумышленникам легко осуществлять фишинг, сбор и повторное использование вредоносного ПО.

В Android ведется работа над созданием API Credential Manager для упрощения процесса входа в систему и устранения угроз безопасности за счет поддержки паролей — отраслевого стандарта нового поколения для аутентификации без пароля .

Менеджер учетных данных объединяет поддержку паролей с традиционными методами аутентификации, такими как пароли, вход через Google и т. д.

Пользователи смогут создавать пароли и сохранять их в Google Password Manager, который будет синхронизировать эти пароли на всех устройствах Android, где пользователь авторизован. Для входа в систему необходимо создать пароль, связать его с учетной записью пользователя и сохранить его открытый ключ на сервере.

В этом практическом занятии вы узнаете, как зарегистрироваться, используя ключи доступа и пароль с помощью API Credential Manager, и использовать их для аутентификации в будущем. Предусмотрены 2 сценария аутентификации:

  • Регистрация: с использованием паролей и пароля.
  • Вход в систему: с использованием ключей доступа и сохраненного пароля.

Предварительные требования

  • Базовое понимание того, как запускать приложения в Android Studio.
  • Базовое понимание процесса аутентификации в приложениях Android.
  • Базовое понимание работы паролей .

Что вы узнаете

  • Как создать пароль.
  • Как сохранить пароль в менеджере паролей.
  • Как аутентифицировать пользователей с помощью ключа доступа или сохраненного пароля.

Что вам понадобится

Одна из следующих комбинаций устройств:

  • Устройство Android, работающее под управлением Android 9 или выше (для паролей) и Android 4.4 или выше (для аутентификации по паролю через API Credential Manager).
  • Устройство предпочтительно с биометрическим датчиком.
  • Обязательно зарегистрируйте блокировку экрана (биометрическую или иную).
  • Версия плагина Kotlin: 1.8.10

2. Настройка

Для проверки связи с веб-сайтом в этом примере приложения требуется, чтобы менеджер учетных данных проверил связь и продолжил работу, поэтому идентификатор rp, используемый в фиктивных ответах, берется с фиктивного стороннего сервера. Если вы хотите попробовать свой собственный фиктивный ответ, добавьте домен вашего приложения и не забудьте завершить привязку цифрового актива, как указано здесь .

Используйте тот же файл debug.keystore, что и в проекте, для сборки отладочной и релизной версий, чтобы проверить привязку цифровых активов имени пакета и хеш-суммы на вашем тестовом сервере. (Это уже сделано для примера приложения в файле build.gradle).

  1. Клонируйте этот репозиторий на свой ноутбук из ветки credman_codelab : https://github.com/android/identity-samples/tree/credman_codelab
git clone -b credman_codelab https://github.com/android/identity-samples.git
  1. Перейдите в модуль CredentialManager и откройте проект в Android Studio.

Давайте посмотрим на начальное состояние приложения.

Чтобы увидеть, как работает приложение в исходном состоянии, выполните следующие шаги:

  1. Запустите приложение.
  2. Вы видите главный экран с кнопками регистрации и входа в систему. Эти кнопки пока ничего не делают, но мы включим их функциональность в следующих разделах.

7a6fe80f4cf877a8.jpeg

3. Добавить возможность регистрации с помощью паролей.

При регистрации новой учетной записи в приложении Android, использующем API Credential Manager, пользователи могут создать пароль для своей учетной записи. Этот пароль будет надежно храниться в выбранном пользователем поставщике учетных данных и использоваться для последующих входов в систему, не требуя от пользователя каждый раз вводить свой пароль.

Теперь вам нужно будет создать пароль и зарегистрировать учетные данные пользователя с помощью биометрии/блокировки экрана.

Зарегистрируйтесь с помощью пароля

В коде файла CredentialManager/app/src/main/java/com/google/credentialmanager/sample/SignUpScreen.kt определено текстовое поле "имя пользователя" и кнопка для регистрации с помощью пароля.

1f4c50daa2551f1.jpeg

Определите лямбда-функцию createCredential() для использования в моделях представления.

Для работы объектов менеджера учетных данных требуется передача Activity , связанной со Screen. Однако операции менеджера учетных данных обычно запускаются в View Models, и не рекомендуется ссылаться на Activity внутри View Models. Поэтому мы определяем функции менеджера учетных данных в отдельном файле CredentialManagerUtil.kt и ссылаемся на них в соответствующих Screens, которые затем передают их в свои View Models в качестве обратных вызовов через лямбда-функции.

Найдите комментарий TODO в функции createCredential() в файле CredentialManagerUtil.kt и вызовите функцию CredentialManager.create() :

CredentialManagerUtil.kt

suspend fun createCredential(
    activity: Activity,
    request: CreateCredentialRequest
): CreateCredentialResponse {
    TODO("Create a CredentialManager object and call createCredential() with a CreateCredentialRequest")
    val credentialManager = CredentialManager.create(activity)
    return credentialManager.createCredential(activity, request)
}

Передайте запрос и другие данные JSON из ответа в вызов функции createPasskey().

Перед созданием ключа доступа необходимо запросить у сервера необходимую информацию для передачи в API диспетчера учетных данных во время вызова функции createCredential ().

В ресурсах вашего проекта уже есть фиктивный ответ под названием RegFromServer.txt , который возвращает необходимые параметры для данного практического занятия.

  • В вашем приложении перейдите в файл SignUpViewModel.kt и найдите метод signUpWithPasskeys , где вы напишете логику для создания пароля и входа пользователя. Этот метод находится в том же классе.
  • Найдите блок комментариев TODO , чтобы create a CreatePublicKeyCredentialRequest() , и замените его следующим кодом:

SignUpViewModel.kt

TODO("Create a CreatePublicKeyCredentialRequest() with necessary registration json from server")
    val request = CreatePublicKeyCredentialRequest(
        jsonProvider.fetchRegistrationJson()
            .replace("<userId>", getEncodedUserId())
            .replace("<userName>", _username.value)
            .replace("<userDisplayName>", _username.value)
            .replace("<challenge>", getEncodedChallenge())
    )

Метод jsonProvider.fetchRegistrationJsonFromServer() считывает эмулированный JSON-ответ сервера PublicKeyCredentialCreationOptions из ресурсов и возвращает регистрационный JSON, который будет передан при создании ключа доступа. Мы заменяем некоторые значения-заполнители пользовательскими записями из нашего приложения и некоторыми фиктивными полями:

  • Этот JSON-файл неполный и содержит 4 поля, которые необходимо заменить.
  • Идентификатор пользователя (UserId) должен быть уникальным, чтобы пользователь мог создать несколько паролей (при необходимости). Замените <userId> сгенерированным значением userId .
  • <challenge> также должно быть уникальным, поэтому вы будете генерировать случайный уникальный запрос. Этот метод уже есть в вашем коде.

В ответе реального сервера на запрос PublicKeyCredentialCreationOptions могут быть возвращены дополнительные параметры. Пример некоторых из этих полей приведен ниже:

{
  "challenge": String,
  "rp": {
    "name": String,
    "id": String
  },
  "user": {
    "id": String,
    "name": String,
    "displayName": String
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

В следующей таблице приведены некоторые важные параметры объекта PublicKeyCredentialCreationOptions :

Параметры

Описания

challenge

Случайная строка, сгенерированная сервером, содержащая достаточно энтропии, чтобы её угадывание было невозможным. Её длина должна составлять не менее 16 байт. Это обязательное требование, но оно не используется во время регистрации, за исключением случаев аттестации .

user.id

Уникальный идентификатор пользователя. Это значение не должно содержать личную информацию, например, адреса электронной почты или имена пользователей. Хорошо подойдет случайное 16-байтовое значение, генерируемое для каждой учетной записи.

user.name

В этом поле должен содержаться уникальный идентификатор учетной записи, который пользователь сможет распознать, например, адрес электронной почты или имя пользователя. Он будет отображаться в окне выбора учетной записи. (Если используется имя пользователя, используйте то же значение, что и при аутентификации по паролю.)

user.displayName

Это поле предназначено для необязательного, более удобного для пользователя названия учетной записи.

rp.id

Объект «Зависимая сторона» соответствует данным вашей заявки. Он обладает следующими атрибутами:

  • name (обязательно): название вашего приложения
  • ID (необязательный): соответствует домену или поддомену. Если отсутствует, используется текущий домен.
  • icon (необязательно).

pubKeyCredParams

Список допустимых алгоритмов и типов ключей. Этот список должен содержать как минимум один элемент.

excludeCredentials

Пользователь, пытающийся зарегистрировать устройство, мог зарегистрировать и другие устройства. Чтобы ограничить создание нескольких учетных данных для одной и той же учетной записи на одном аутентификаторе, вы можете игнорировать эти устройства. Член transports , если он предоставлен, должен содержать результат вызова getTransports() во время регистрации каждой учетной записи.

authenticatorSelection.authenticatorAttachment

Указывает, следует ли подключать устройство к платформе, или нет, или же это не требуется. Установите это значение равным platform . Это означает, что вам нужен аутентификатор, встроенный в устройство платформы, и пользователю не будет предлагаться вставить, например, USB-ключ безопасности.

residentKey

Укажите значение, required для создания пароля.

Создать учетные данные

  1. После создания объекта CreatePublicKeyCredentialRequest() необходимо вызвать метод createCredential() с созданным запросом.

SignUpViewModel.kt

try {
   TODO("Call createCredential() with createPublicKeyCredentialRequest")
   createCredential(request)
   TODO("Complete the registration process after sending public key credential to your server and let the user in")

} catch (e: CreateCredentialException) {
   handlePasskeyFailure(e)
}

  • Вы управляете видимостью отображаемых представлений и обрабатываете исключения, если запрос не удается или выполняется по какой-либо причине. Здесь сообщения об ошибках записываются в журнал и отображаются в приложении в диалоговом окне ошибки. Вы можете просмотреть полный журнал ошибок через Android Studio или команду adb debug .

1ea8ace66135de1e.png

  1. Наконец, вам необходимо завершить процесс регистрации. Приложение отправляет на сервер открытый ключ, который регистрирует его в качестве текущего пользователя.

Здесь мы использовали фиктивный сервер, поэтому просто возвращаем true, указывая на то, что сервер сохранил зарегистрированный открытый ключ для будущей аутентификации и проверки. Вы можете узнать больше о регистрации пароля на стороне сервера для вашей собственной реализации.

Внутри метода signUpWithPasskeys() найдите соответствующий комментарий и замените его следующим кодом:

SignUpViewModel.kt

try {
    createCredential(request)
    TODO("Complete the registration process after sending public key credential to your server and let the user in")
registerResponse()
    DataProvider.setSignedInThroughPasskeys(true)
    _navigationEvent.emit(NavigationEvent.NavigateToHome(signedInWithPasskeys = true))
} catch (e: CreateCredentialException) {
   handlePasskeyFailure(e)
}
  • registerResponse() возвращает true указывая на то, что фиктивный сервер сохранил открытый ключ для дальнейшего использования.
  • Установите флаг setSignedInThroughPasskeys в true .
  • После авторизации пользователь будет перенаправлен на главный экран.

Объект PublicKeyCredential может содержать больше полей. Пример таких полей показан ниже:

{
  "id": String,
  "rawId": String,
  "type": "public-key",
  "response": {
    "clientDataJSON": String,
    "attestationObject": String,
  }
}

В следующей таблице приведены некоторые важные параметры объекта PublicKeyCredential :

Параметры

Описания

id

Идентификатор созданного пароля, закодированный в Base64URL. Этот идентификатор помогает браузеру определить, есть ли соответствующий пароль на устройстве при аутентификации. Это значение должно храниться в базе данных на бэкэнде.

rawId

Объект ArrayBuffer представляющий собой версию идентификатора учетных данных.

response.clientDataJSON

Объект ArrayBuffer содержит закодированные данные клиента.

response.attestationObject

Объект аттестации, закодированный в формате ArrayBuffer . Он содержит важную информацию, такую ​​как идентификатор участника проверки (RP ID), флаги и открытый ключ.

Запустите приложение, и вы сможете нажать кнопку « Зарегистрироваться с помощью паролей» и создать пароль.

4. Сохраните пароль в поставщике учетных данных.

В этом приложении на экране регистрации уже реализована форма регистрации с именем пользователя и паролем для демонстрационных целей.

Для сохранения учетных данных пароля пользователя в его поставщике паролей вам потребуется реализовать метод CreatePasswordRequest , который будет передаваться в createCredential() для сохранения пароля.