Создайте пароль для входа в систему без пароля

Ключи доступа делают учетные записи пользователей более безопасными, простыми и удобными в использовании.

Эйдзи Китамура
Эйдзи Китамура

Использование ключей доступа вместо паролей — отличный способ для веб-сайтов сделать свои учетные записи пользователей более безопасными, простыми и легкими в использовании и не требующими паролей. С помощью ключа доступа пользователь может войти на веб-сайт или в приложение, просто используя свой отпечаток пальца, лицо или PIN-код устройства.

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

Как это работает

Пользователя можно попросить создать пароль в одной из следующих ситуаций:

  • Когда пользователь входит в систему, используя пароль.
  • Когда пользователь входит в систему, используя ключ доступа с другого устройства (т. е. authenticatorAttachment является cross-platform ).
  • На специальной странице, где пользователи могут управлять своими ключами доступа.

Чтобы создать ключ доступа, вы используете API WebAuthn .

Четыре компонента процесса регистрации ключа доступа:

  • Бэкэнд : ваш внутренний сервер, на котором хранится база данных учетных записей, хранящая открытый ключ и другие метаданные о ключе доступа.
  • Фронтенд : ваш фронтенд, который взаимодействует с браузером и отправляет запросы на выборку на бэкэнд.
  • Браузер : браузер пользователя, в котором работает ваш Javascript.
  • Аутентификатор : Аутентификатор пользователя, который создает и сохраняет ключ доступа. Это может быть то же устройство, что и браузер (например, при использовании Windows Hello) или другое устройство, например телефон.
Схема регистрации пароля

Процедура добавления нового ключа доступа к существующей учетной записи пользователя выглядит следующим образом:

  1. Пользователь авторизуется на сайте.
  2. После того как пользователь вошел в систему, он запрашивает создание ключа доступа во внешнем интерфейсе, например, нажав кнопку «Создать ключ доступа».
  3. Интерфейсный интерфейс запрашивает информацию у внутреннего интерфейса для создания ключа доступа, например информацию о пользователе, запрос и идентификаторы учетных данных, которые необходимо исключить.
  4. Интерфейс вызывает navigator.credentials.create() для создания ключа доступа. Этот вызов возвращает обещание.
  5. Ключ доступа создается после того, как пользователь дает согласие на использование блокировки экрана устройства. Промис разрешается, и учетные данные открытого ключа возвращаются во внешний интерфейс.
  6. Внешний интерфейс отправляет учетные данные открытого ключа на серверную часть и сохраняет идентификатор учетных данных и открытый ключ, связанный с учетной записью пользователя, для будущих аутентификаций.

Совместимость

WebAuthn поддерживается большинством браузеров, но есть небольшие недостатки. Обратитесь к разделу «Поддержка устройств» — passkeys.dev , чтобы узнать, какая комбинация браузеров и операционных систем поддерживает создание ключа доступа.

Создать новый ключ доступа

Вот как должен работать интерфейс по запросу на создание нового ключа доступа.

Обнаружение функций

Прежде чем отображать кнопку «Создать новый ключ доступа», проверьте:

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.  
// `​​isConditionalMediationAvailable` means the feature detection is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if user verifying platform authenticator is available.  
  Promise.all([  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),  
    PublicKeyCredential.​​isConditionalMediationAvailable(),  
  ]).then(results => {  
    if (results.every(r => r === true)) {  
      // Display "Create a new passkey" button  
    }  
  });  
}  

Пока все условия не будут выполнены, ключи доступа не будут поддерживаться в этом браузере. До этого момента кнопка «Создать новый ключ доступа» не должна отображаться.

Получите важную информацию из серверной части

Когда пользователь нажимает кнопку, получает важную информацию для вызова navigator.credentials.create() из бэкэнда:

  • challenge : вызов, сгенерированный сервером в ArrayBuffer для этой регистрации. Это необходимо, но не используется во время регистрации, если не проводится аттестация — сложная тема, здесь не рассматриваемая.
  • user.id : уникальный идентификатор пользователя. Это значение должно быть ArrayBuffer, которое не содержит личной информации, например адресов электронной почты или имен пользователей. Случайное 16-байтовое значение, созданное для каждой учетной записи, подойдет.
  • user.name : это поле должно содержать уникальный идентификатор учетной записи, которую узнает пользователь, например адрес электронной почты или имя пользователя. Это будет отображаться в средстве выбора учетной записи. (При использовании имени пользователя используйте то же значение, что и при аутентификации по паролю.)
  • user.displayName : это поле является обязательным и более удобным для пользователя именем учетной записи. Оно не обязательно должно быть уникальным и может быть именем, выбранным пользователем. Если на вашем сайте нет подходящего значения для включения сюда, передайте пустую строку. Это может отображаться в средстве выбора учетной записи в зависимости от браузера.
  • excludeCredentials : предотвращает регистрацию одного и того же устройства, предоставляя список уже зарегистрированных идентификаторов учетных данных. Член transports , если он предусмотрен, должен содержать результат вызова getTransports() во время регистрации каждого учетного документа.

Вызовите WebAuthn API, чтобы создать ключ доступа.

Вызовите navigator.credentials.create() , чтобы создать новый ключ доступа. API возвращает обещание, ожидая взаимодействия пользователя с отображением модального диалога.

const publicKeyCredentialCreationOptions = {
  challenge: *****,
  rp: {
    name: "Example",
    id: "example.com",
  },
  user: {
    id: *****,
    name: "john78",
    displayName: "John",
  },
  pubKeyCredParams: [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}],
  excludeCredentials: [{
    id: *****,
    type: 'public-key',
    transports: ['internal'],
  }],
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
  }
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions
});

// Encode and send the credential to the server for verification.  

Параметры, не описанные выше:

  • rp.id : идентификатор RP — это домен, и веб-сайт может указать либо свой домен, либо регистрируемый суффикс. Например, если источником RP является https://login.example.com:1337 , идентификатор RP может быть либо login.example.com , либо example.com . Если идентификатор RP указан как example.com , пользователь может пройти аутентификацию на login.example.com или на любых поддоменах на example.com .

  • rp.name : имя RP.

  • pubKeyCredParams : в этом поле указаны поддерживаемые RP алгоритмы открытого ключа. Мы рекомендуем установить его на [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}] . Это определяет поддержку ECDSA с P-256 и RSA PKCS#1, и их поддержка обеспечивает полный охват.

  • authenticatorSelection.authenticatorAttachment : установите для этого параметра значение "platform" , если создание ключа доступа является обновлением пароля, например, в рекламной акции после входа в систему. "platform" указывает, что RP нужен аутентификатор платформы (аутентификатор, встроенный в устройство платформы), который не будет запрашивать вставку, например, ключа безопасности USB. У пользователя есть более простой вариант создания пароля.

  • authenticatorSelection.requireResidentKey : установите для него логическое значение «истина». Обнаруживаемые учетные данные (резидентный ключ) сохраняют информацию о пользователе в ключе доступа и позволяют пользователям выбирать учетную запись при аутентификации.

  • authenticatorSelection.userVerification : указывает, является ли проверка пользователя с использованием блокировки экрана устройства "required" , "preferred" или "discouraged" . По умолчанию установлено значение "preferred" , что означает, что аутентификатор может пропустить проверку пользователя. Установите для этого параметра значение "preferred" или опустите это свойство.

Отправьте возвращенные учетные данные открытого ключа на серверную часть.

После того как пользователь дает согласие на использование блокировки экрана устройства, создается ключ доступа и разрешается обещание, возвращающее объект PublicKeyCredential во внешний интерфейс.

Обещание может быть отклонено по разным причинам. Вы можете обработать эти ошибки, проверив свойство name объекта Error :

  • InvalidStateError : на устройстве уже существует ключ доступа. Пользователю не будет показано диалоговое окно с ошибкой, и сайт не должен рассматривать это как ошибку — пользователь хотел, чтобы локальное устройство было зарегистрировано, и оно было зарегистрировано.
  • NotAllowedError : пользователь отменил операцию.
  • Другие исключения : Произошло что-то неожиданное. Браузер показывает пользователю диалоговое окно с ошибкой.

Объект учетных данных открытого ключа содержит следующие свойства:

  • id : идентификатор созданного ключа доступа в кодировке Base64URL. Этот идентификатор помогает браузеру определить, имеется ли на устройстве соответствующий ключ доступа при аутентификации. Это значение необходимо сохранить в базе данных на серверной стороне.
  • rawId : версия идентификатора учетных данных ArrayBuffer.
  • response.clientDataJSON : данные клиента, закодированные в ArrayBuffer.
  • response.attestationObject : объект аттестации, закодированный ArrayBuffer. Он содержит важную информацию, такую ​​как идентификатор RP, флаги и открытый ключ.
  • authenticatorAttachment : возвращает "platform" , когда эти учетные данные создаются на устройстве с ключом доступа.
  • type : в этом поле всегда установлено значение "public-key" .

Если вы используете библиотеку для обработки объекта учетных данных открытого ключа на серверной стороне, мы рекомендуем отправить на серверную часть весь объект после его частичного кодирования с помощью base64url.

Сохраните учетные данные

Получив учетные данные открытого ключа на серверной стороне, передайте их в библиотеку FIDO для обработки объекта.

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

  • Идентификатор учетных данных (первичный ключ)
  • ID пользователя
  • Открытый ключ

Учетные данные с открытым ключом также включают следующую информацию, которую вы, возможно, захотите сохранить в базе данных:

Чтобы аутентифицировать пользователя, прочтите «Войти в систему с помощью пароля через автозаполнение формы» .

Ресурсы