Создайте свое первое приложение WebAuthn

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

API-интерфейс веб-аутентификации, также известный как WebAuthn, позволяет создавать и использовать учетные данные с открытым ключом на уровне источника для аутентификации пользователей.

API поддерживает использование аутентификаторов BLE, NFC и USB-роуминга U2F или FIDO2, также известных как ключи безопасности, а также аутентификатора платформы, который позволяет пользователям аутентифицироваться с помощью отпечатков пальцев или блокировки экрана.

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

Предпосылки

  • Базовое понимание того, как работает WebAuthn
  • Базовые навыки программирования на JavaScript

Что ты будешь делать

  • Создайте веб-сайт с простой функцией повторной аутентификации, использующей датчик отпечатков пальцев.

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

  • Одно из следующих устройств:
    • Android-устройство, желательно с биометрическим датчиком
    • iPhone или iPad с Touch ID или Face ID на iOS 14 или выше
    • MacBook Pro или Air с Touch ID на macOS Big Sur или выше
    • Windows 10 19H1 или выше с настройкой Windows Hello
  • Один из следующих браузеров:
    • Google Chrome 67 или выше
    • Microsoft Edge 85 или выше
    • Сафари 14 или выше

2. Настройте

В этой кодлабе вы используете сервис под названием glitch . Здесь вы можете редактировать клиентский и серверный код с помощью JavaScript и мгновенно развертывать их.

Перейдите к https://glitch.com/edit/#!/webauthn-codelab-start .

Увидеть как это работает

Выполните следующие действия, чтобы увидеть начальное состояние веб-сайта:

  1. Нажмите 62bb7a6aac381af8.png Показать > 3343769d04c09851.png В новом окне , чтобы увидеть живой веб-сайт .
  2. Введите имя пользователя по вашему выбору и нажмите « Далее ».
  3. Введите пароль и нажмите Войти .

Пароль игнорируется, но вы все еще аутентифицированы. Вы попадаете на главную страницу.

  1. Щелкните Try reauth и повторите второй, третий и четвертый шаги.
  2. Щелкните Выйти .

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

Смешайте код

  1. Перейдите к WebAuthn/FIDO2 API Codelab .
  2. Нажмите на название вашего проекта > Remix Project . 306122647ce93305.png чтобы разветвить проект и продолжить работу с собственной версией по новому URL-адресу.

8d42bd24f0fd185c.png

3. Зарегистрируйте учетные данные с отпечатком пальца

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

Вы добавляете эту функцию на /home страницу:

260aab9f1a2587a7.png

Создать функцию registerCredential()

Создайте функцию registerCredential() , которая регистрирует новые учетные данные.

общедоступный/client.js

export const registerCredential = async () => {  }; 

Получите вызов и другие параметры с конечной точки сервера.

Прежде чем попросить пользователя зарегистрировать новые учетные данные, запросите, чтобы сервер вернул параметры для передачи в WebAuthn, включая вызов. К счастью, у вас уже есть конечная точка сервера, которая отвечает такими параметрами.

Добавьте следующий код в registerCredential() .

общедоступный/client.js

const opts = {   attestation: 'none',   authenticatorSelection: {     authenticatorAttachment: 'platform',     userVerification: 'required',     requireResidentKey: false   } };  const options = await _fetch('/auth/registerRequest', opts); 

Протокол между сервером и клиентом не является частью спецификации WebAuthn. Однако эта кодовая лаборатория предназначена для согласования со спецификацией WebAuthn, а объект JSON, который вы передаете на сервер, очень похож на PublicKeyCredentialCreationOptions , поэтому он интуитивно понятен для вас. Следующая таблица содержит важные параметры, которые вы можете передать на сервер, и объясняет, что они делают:

Параметры

Описания

attestation

Предпочтение в отношении передачи аттестации — none , indirect или direct . Не выбирайте none , если он вам не нужен.

excludeCredentials

Массив PublicKeyCredentialDescriptor , чтобы аутентификатор мог избежать создания дубликатов.

authenticatorSelection

authenticatorAttachment

Фильтровать доступные аутентификаторы. Если вы хотите, чтобы к устройству был подключен аутентификатор, используйте « platform ». Для перемещаемых аутентификаторов используйте « cross-platform ».

userVerification

Определите, является ли проверка локального пользователя аутентификатора « required », « preferred » или « discouraged ». Если вы хотите аутентификацию по отпечатку пальца или блокировке экрана, используйте « required ».

requireResidentKey

Используйте true , если созданные учетные данные должны быть доступны для будущего пользовательского интерфейса средства выбора учетной записи.

Чтобы узнать больше об этих опциях, см . 5.4. Параметры создания учетных данных (словарь PublicKeyCredentialCreationOptions ) .

Ниже приведены примеры параметров, которые вы получаете от сервера.

{   "rp": {     "name": "WebAuthn Codelab",     "id": "webauthn-codelab.glitch.me"   },   "user": {     "displayName": "User Name",     "id": "...",     "name": "test"   },   "challenge": "...",   "pubKeyCredParams": [     {       "type": "public-key",       "alg": -7     }, {       "type": "public-key",       "alg": -257     }   ],   "timeout": 1800000,   "attestation": "none",   "excludeCredentials": [     {       "id": "...",       "type": "public-key",       "transports": [         "internal"       ]     }   ],   "authenticatorSelection": {     "authenticatorAttachment": "platform",     "userVerification": "required"   } } 

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

  1. Поскольку эти параметры доставляются закодированными для прохождения через протокол HTTP, преобразуйте некоторые параметры обратно в двоичные, в частности, user.id , challenge и экземпляры id , включенные в массив excludeCredentials :

общедоступный/client.js

options.user.id = base64url.decode(options.user.id); options.challenge = base64url.decode(options.challenge);  if (options.excludeCredentials) {   for (let cred of options.excludeCredentials) {     cred.id = base64url.decode(cred.id);   } } 
  1. Вызовите метод navigator.credentials.create() , чтобы создать новые учетные данные.

С помощью этого вызова браузер взаимодействует с аутентификатором и пытается проверить личность пользователя с помощью UVPA.

общедоступный/client.js

const cred = await navigator.credentials.create({   publicKey: options, }); 

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

Зарегистрируйте учетные данные на конечной точке сервера

Вот пример объекта учетных данных, который вы должны были получить.

{   "id": "...",   "rawId": "...",   "type": "public-key",   "response": {     "clientDataJSON": "...",     "attestationObject": "..."   } } 
  1. Как и в случае, когда вы получили объект опции для регистрации учетных данных, закодируйте двоичные параметры учетных данных, чтобы их можно было доставить на сервер в виде строки:

общедоступный/client.js

const credential = {}; credential.id = cred.id; credential.rawId = base64url.encode(cred.rawId); credential.type = cred.type;  if (cred.response) {   const clientDataJSON =     base64url.encode(cred.response.clientDataJSON);   const attestationObject =     base64url.encode(cred.response.attestationObject);   credential.response = {     clientDataJSON,     attestationObject,   }; } 
  1. Сохраните идентификатор учетных данных локально, чтобы вы могли использовать его для аутентификации, когда пользователь вернется:

общедоступный/client.js

localStorage.setItem(`credId`, credential.id); 
  1. Отправьте объект на сервер и, если он вернет HTTP code 200 , считайте, что новые учетные данные успешно зарегистрированы.

общедоступный/client.js

return await _fetch('/auth/registerResponse' , credential); 

Теперь у вас есть полная функция registerCredential() !

Окончательный код для этого раздела

общедоступный/client.js

... export const registerCredential = async () => {   const opts = {     attestation: 'none',     authenticatorSelection: {       authenticatorAttachment: 'platform',       userVerification: 'required',       requireResidentKey: false     }   };    const options = await _fetch('/auth/registerRequest', opts);    options.user.id = base64url.decode(options.user.id);   options.challenge = base64url.decode(options.challenge);    if (options.excludeCredentials) {     for (let cred of options.excludeCredentials) {       cred.id = base64url.decode(cred.id);     }   }      const cred = await navigator.credentials.create({     publicKey: options   });    const credential = {};   credential.id =     cred.id;   credential.rawId =  base64url.encode(cred.rawId);   credential.type =   cred.type;    if (cred.response) {     const clientDataJSON =       base64url.encode(cred.response.clientDataJSON);     const attestationObject =       base64url.encode(cred.response.attestationObject);     credential.response = {       clientDataJSON,       attestationObject     };   }    localStorage.setItem(`credId`, credential.id);      return await _fetch('/auth/registerResponse' , credential); }; ... 

4. Создайте пользовательский интерфейс для регистрации, получения и удаления учетных данных.

Хорошо иметь список зарегистрированных учетных данных и кнопки для их удаления.

9b5b5ae4a7b316bd.png

Заполнитель пользовательского интерфейса сборки

Добавьте пользовательский интерфейс для списка учетных данных и кнопку для регистрации новых учетных данных. В зависимости от того, доступна функция или нет, вы удаляете hidden класс либо из предупреждающего сообщения, либо из кнопки для регистрации новых учетных данных. ul#list — это заполнитель для добавления списка зарегистрированных учетных данных.

просмотры/home.html

<p id="uvpa_unavailable" class="hidden">   This device does not support User Verifying Platform Authenticator. You can't register a credential. </p> <h3 class="mdc-typography mdc-typography--headline6">   Your registered credentials: </h3> <section>   <div id="list"></div> </section> <mwc-button id="register" class="hidden" icon="fingerprint" raised>Add a credential</mwc-button> 

Обнаружение признаков и доступность UVPA

Выполните следующие действия, чтобы проверить доступность UVPA:

  1. Изучите window.PublicKeyCredential , чтобы проверить, доступен ли WebAuthn.
  2. Вызовите PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() , чтобы проверить, доступен ли UVPA. Если они доступны, вы показываете кнопку для регистрации новых учетных данных. Если какой-либо из них недоступен, вы показываете предупреждающее сообщение.

просмотры/home.html

const register = document.querySelector('#register');  if (window.PublicKeyCredential) {   PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()   .then(uvpaa => {     if (uvpaa) {       register.classList.remove('hidden');     } else {       document         .querySelector('#uvpa_unavailable')         .classList.remove('hidden');     }   });         } else {   document     .querySelector('#uvpa_unavailable')     .classList.remove('hidden'); } 

Получить и отобразить список учетных данных

  1. Создайте функцию getCredentials() , чтобы вы могли получать зарегистрированные учетные данные и отображать их в списке. К счастью, у вас уже есть удобная конечная точка на сервере /auth/getKeys из которой вы можете получить зарегистрированные учетные данные для вошедшего в систему пользователя.

Возвращаемый JSON включает учетную информацию, такую ​​как id и publicKey . Вы можете создать HTML, чтобы показать их пользователю.

просмотры/home.html

const getCredentials = async () => {   const res = await _fetch('/auth/getKeys');   const list = document.querySelector('#list');   const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`     <div class="mdc-card credential">       <span class="mdc-typography mdc-typography--body2">${cred.credId}</span>       <pre class="public-key">${cred.publicKey}</pre>       <div class="mdc-card__actions">         <mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>       </div>     </div>`) : html`     <p>No credentials found.</p>     `}`;   render(creds, list); }; 
  1. getCredentials() для отображения доступных учетных данных, как только пользователь попадет на страницу /home .

просмотры/home.html

getCredentials(); 

Удалить учетные данные

В список учетных данных вы добавили кнопку для удаления каждого из учетных данных. Вы можете отправить запрос на /auth/removeKey вместе с параметром запроса credId , чтобы удалить их.

общедоступный/client.js

export const unregisterCredential = async (credId) => {   localStorage.removeItem('credId');   return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`); }; 
  1. Добавьте unregisterCredential к существующему оператору import .

просмотры/home.html

import { _fetch, unregisterCredential } from '/client.js'; 
  1. Добавьте функцию, которая будет вызываться, когда пользователь нажимает кнопку « Удалить ».

просмотры/home.html

const removeCredential = async e => {   try {     await unregisterCredential(e.target.id);     getCredentials();   } catch (e) {     alert(e);   } }; 

Зарегистрировать учетные данные

Вы можете вызвать registerCredential() для регистрации новых учетных данных, когда пользователь нажимает Добавить учетные данные .

  1. Добавьте registerCredential к существующему оператору import .

просмотры/home.html

import { _fetch, registerCredential, unregisterCredential } from '/client.js'; 
  1. Вызвать registerCredential() с параметрами navigator.credentials.create() .

Не забудьте обновить список учетных данных, вызвав getCredentials() после регистрации.

просмотры/home.html

register.addEventListener('click', e => {   registerCredential().then(user => {     getCredentials();   }).catch(e => alert(e)); }); 

Теперь вы сможете зарегистрировать новые учетные данные и отобразить информацию о них. Вы можете попробовать это на своем живом веб-сайте.

Окончательный код для этого раздела

просмотры/home.html

...       <p id="uvpa_unavailable" class="hidden">         This device does not support User Verifying Platform Authenticator. You can't register a credential.       </p>       <h3 class="mdc-typography mdc-typography--headline6">         Your registered credentials:       </h3>       <section>         <div id="list"></div>         <mwc-fab id="register" class="hidden" icon="add"></mwc-fab>       </section>       <mwc-button raised><a href="/reauth">Try reauth</a></mwc-button>       <mwc-button><a href="/auth/signout">Sign out</a></mwc-button>     </main>     <script type="module">       import { _fetch, registerCredential, unregisterCredential } from '/client.js';       import { html, render } from 'https://unpkg.com/[email protected]/lit-html.js?module';        const register = document.querySelector('#register');        if (window.PublicKeyCredential) {         PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()         .then(uvpaa => {           if (uvpaa) {             register.classList.remove('hidden');           } else {             document               .querySelector('#uvpa_unavailable')               .classList.remove('hidden');           }         });               } else {         document           .querySelector('#uvpa_unavailable')           .classList.remove('hidden');       }        const getCredentials = async () => {         const res = await _fetch('/auth/getKeys');         const list = document.querySelector('#list');         const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`           <div class="mdc-card credential">             <span class="mdc-typography mdc-typography--body2">${cred.credId}</span>             <pre class="public-key">${cred.publicKey}</pre>             <div class="mdc-card__actions">               <mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>             </div>           </div>`) : html`           <p>No credentials found.</p>           `}`;         render(creds, list);       };        getCredentials();        const removeCredential = async e => {         try {           await unregisterCredential(e.target.id);           getCredentials();         } catch (e) {           alert(e);         }       };        register.addEventListener('click', e => {         registerCredential({           attestation: 'none',           authenticatorSelection: {             authenticatorAttachment: 'platform',             userVerification: 'required',             requireResidentKey: false           }         })         .then(user => {           getCredentials();         })         .catch(e => alert(e));       });     </script> ... 

общедоступный/client.js

... export const unregisterCredential = async (credId) => {   localStorage.removeItem('credId');   return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`); }; ... 

5. Аутентифицируйте пользователя по отпечатку пальца

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

Когда пользователь попадает на страницу /reauth , он видит кнопку Authenticate , если возможна биометрическая аутентификация. Аутентификация с помощью отпечатка пальца (UVPA) начинается, когда они нажимают « Аутентификация » , успешно проходят аутентификацию, а затем попадают на страницу /home . Если биометрическая аутентификация недоступна или биометрическая аутентификация не удалась, пользовательский интерфейс возвращается к использованию существующей формы пароля.

b8770c4e7475b075.png

Создать функцию authenticate()

Создайте функцию с именем authenticate() , которая проверяет личность пользователя с помощью отпечатка пальца. Вы добавляете код JavaScript здесь:

общедоступный/client.js

export const authenticate = async () => {  }; 

Получите вызов и другие параметры с конечной точки сервера.

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

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

общедоступный/client.js

const opts = {};  let url = '/auth/signinRequest'; const credId = localStorage.getItem(`credId`); if (credId) {   url += `?credId=${encodeURIComponent(credId)}`; } 
  1. Прежде чем просить пользователя пройти аутентификацию, попросите сервер отправить запрос и другие параметры. Вызовите _fetch() с opts в качестве аргумента, чтобы отправить запрос POST на сервер.

общедоступный/client.js

const options = await _fetch(url, opts); 

Вот примеры параметров, которые вы должны получить (согласуется с PublicKeyCredentialRequestOptions ).

{   "challenge": "...",   "timeout": 1800000,   "rpId": "webauthn-codelab.glitch.me",   "userVerification": "required",   "allowCredentials": [     {       "id": "...",       "type": "public-key",       "transports": [         "internal"       ]     }   ] } 

Самый важный параметр здесь — allowCredentials . Когда вы получаете параметры от сервера, allowCredentials должен быть либо отдельным объектом в массиве, либо пустым массивом в зависимости от того, найдены ли учетные данные с идентификатором в параметре запроса на стороне сервера.

  1. Разрешите обещание с null значением, когда allowCredentials является пустым массивом, чтобы пользовательский интерфейс возвращался к запросу пароля.
if (options.allowCredentials.length === 0) {   console.info('No registered credentials found.');   return Promise.resolve(null); } 

Локально проверьте пользователя и получите учетные данные

  1. Поскольку эти параметры доставляются закодированными для прохождения через протокол HTTP, преобразуйте некоторые параметры обратно в двоичные, в частности, challenge и экземпляры id , включенные в массив allowCredentials :

общедоступный/client.js

options.challenge = base64url.decode(options.challenge);  for (let cred of options.allowCredentials) {   cred.id = base64url.decode(cred.id); } 
  1. Вызовите метод navigator.credentials.get() , чтобы проверить личность пользователя с помощью UVPA.

общедоступный/client.js

const cred = await navigator.credentials.get({   publicKey: options }); 

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

Подтвердите учетные данные

Вот пример объекта PublicKeyCredential ( responseAuthenticatorAssertionResponse ), который вы должны были получить:

{   "id": "...",   "type": "public-key",   "rawId": "...",   "response": {     "clientDataJSON": "...",     "authenticatorData": "...",     "signature": "...",     "userHandle": ""   } } 
  1. Закодируйте двоичные параметры учетных данных, чтобы их можно было доставить на сервер в виде строки:

общедоступный/client.js

const credential = {}; credential.id = cred.id; credential.type = cred.type; credential.rawId = base64url.encode(cred.rawId);  if (cred.response) {   const clientDataJSON =     base64url.encode(cred.response.clientDataJSON);   const authenticatorData =     base64url.encode(cred.response.authenticatorData);   const signature =     base64url.encode(cred.response.signature);   const userHandle =     base64url.encode(cred.response.userHandle);   credential.response = {     clientDataJSON,     authenticatorData,     signature,     userHandle,   }; } 
  1. Отправьте объект на сервер и, если он вернет HTTP code 200 , считайте, что пользователь успешно выполнил вход:

общедоступный/client.js

return await _fetch(`/auth/signinResponse`, credential); 

Теперь у вас есть полная функция authentication() !

Окончательный код для этого раздела

общедоступный/client.js

... export const authenticate = async () => {   const opts = {};    let url = '/auth/signinRequest';   const credId = localStorage.getItem(`credId`);   if (credId) {     url += `?credId=${encodeURIComponent(credId)}`;   }      const options = await _fetch(url, opts);      if (options.allowCredentials.length === 0) {     console.info('No registered credentials found.');     return Promise.resolve(null);   }    options.challenge = base64url.decode(options.challenge);    for (let cred of options.allowCredentials) {     cred.id = base64url.decode(cred.id);   }    const cred = await navigator.credentials.get({     publicKey: options   });    const credential = {};   credential.id = cred.id;   credential.type = cred.type;   credential.rawId = base64url.encode(cred.rawId);    if (cred.response) {     const clientDataJSON =       base64url.encode(cred.response.clientDataJSON);     const authenticatorData =       base64url.encode(cred.response.authenticatorData);     const signature =       base64url.encode(cred.response.signature);     const userHandle =       base64url.encode(cred.response.userHandle);     credential.response = {       clientDataJSON,       authenticatorData,       signature,       userHandle,     };   }    return await _fetch(`/auth/signinResponse`, credential); }; ... 

6. Включите повторную аутентификацию

Построить пользовательский интерфейс

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

  • УФПА недоступен.
  • Пользователь еще не зарегистрировал учетные данные на своем устройстве.
  • Хранилище очищено, и устройство больше не помнит идентификатор учетных данных.
  • Пользователь не может подтвердить свою личность по какой-либо причине, например, когда его палец мокрый или он носит маску.

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

19da999b0145054.png

  1. Добавьте пользовательский интерфейс, чтобы показать кнопку аутентификации, которая вызывает биометрическую аутентификацию в дополнение к форме пароля.

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

просмотры/reauth.html

<div id="uvpa_available" class="hidden">   <h2>     Verify your identity   </h2>   <div>     <mwc-button id="reauth" raised>Authenticate</mwc-button>   </div>   <div>     <mwc-button id="cancel">Sign-in with password</mwc-button>   </div> </div> 
  1. Добавьте class="hidden" к форме:

просмотры/reauth.html

<form id="form" method="POST" action="/auth/password" class="hidden"> 

Обнаружение признаков и доступность UVPA

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

  • WebAuthn недоступен.
  • УФПА недоступно.
  • Идентификатор учетных данных для этого UVPA невозможно обнаружить.

Выборочно показать кнопку аутентификации или скрыть ее:

просмотры/reauth.html

if (window.PublicKeyCredential) {   PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()   .then(uvpaa => {     if (uvpaa && localStorage.getItem(`credId`)) {       document         .querySelector('#uvpa_available')         .classList.remove('hidden');     } else {       form.classList.remove('hidden');     }   });         } else {   form.classList.remove('hidden'); } 

Возврат к форме пароля

Пользователь также должен иметь возможность выбрать вход с паролем.

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

просмотры/reauth.html

const cancel = document.querySelector('#cancel'); cancel.addEventListener('click', e => {   form.classList.remove('hidden');   document     .querySelector('#uvpa_available')     .classList.add('hidden'); }); 

c4a82800889f078c.png

Активировать биометрическую аутентификацию

Наконец, включите биометрическую аутентификацию.

  1. Добавьте authenticate к существующему оператору import :

просмотры/reauth.html

import { _fetch, authenticate } from '/client.js'; 
  1. Вызовите authenticate() , когда пользователь нажимает Authenticate , чтобы начать биометрическую аутентификацию.

Убедитесь, что ошибка биометрической аутентификации возвращается к форме пароля.

просмотры/reauth.html

const button = document.querySelector('#reauth'); button.addEventListener('click', e => {   authenticate().then(user => {     if (user) {       location.href = '/home';     } else {       throw 'User not found.';     }   }).catch(e => {     console.error(e.message || e);     alert('Authentication failed. Use password to sign-in.');     form.classList.remove('hidden');     document.querySelector('#uvpa_available').classList.add('hidden');   });         }); 

Окончательный код для этого раздела

просмотры/reauth.html

...     <main class="content">       <div id="uvpa_available" class="hidden">         <h2>           Verify your identity         </h2>         <div>           <mwc-button id="reauth" raised>Authenticate</mwc-button>         </div>         <div>           <mwc-button id="cancel">Sign-in with password</mwc-button>         </div>       </div>       <form id="form" method="POST" action="/auth/password" class="hidden">         <h2>           Enter a password         </h2>         <input type="hidden" name="username" value="{{username}}" />         <div class="mdc-text-field mdc-text-field--filled">           <span class="mdc-text-field__ripple"></span>           <label class="mdc-floating-label" id="password-label">password</label>           <input type="password" class="mdc-text-field__input" aria-labelledby="password-label" name="password" />           <span class="mdc-line-ripple"></span>         </div>         <input type="submit" class="mdc-button mdc-button--raised" value="Sign-In" />         <p class="instructions">password will be ignored in this demo.</p>       </form>     </main>     <script src="https://unpkg.com/[email protected]/dist/material-components-web.min.js"></script>     <script type="module">       new mdc.textField.MDCTextField(document.querySelector('.mdc-text-field'));       import { _fetch, authenticate } from '/client.js';       const form = document.querySelector('#form');       form.addEventListener('submit', e => {         e.preventDefault();         const form = new FormData(e.target);         const cred = {};         form.forEach((v, k) => cred[k] = v);         _fetch(e.target.action, cred)         .then(user => {           location.href = '/home';         })         .catch(e => alert(e));       });        if (window.PublicKeyCredential) {         PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()         .then(uvpaa => {           if (uvpaa && localStorage.getItem(`credId`)) {             document               .querySelector('#uvpa_available')               .classList.remove('hidden');           } else {             form.classList.remove('hidden');           }         });               } else {         form.classList.remove('hidden');       }        const cancel = document.querySelector('#cancel');       cancel.addEventListener('click', e => {         form.classList.remove('hidden');         document           .querySelector('#uvpa_available')           .classList.add('hidden');       });        const button = document.querySelector('#reauth');       button.addEventListener('click', e => {         authenticate().then(user => {           if (user) {             location.href = '/home';           } else {             throw 'User not found.';           }         }).catch(e => {           console.error(e.message || e);           alert('Authentication failed. Use password to sign-in.');           form.classList.remove('hidden');           document.querySelector('#uvpa_available').classList.add('hidden');         });               });     </script> ... 

7. Поздравляем!

Вы закончили эту лабораторную работу!

Учить больше

Отдельное спасибо Юрию Акерманну из FIDO Alliance за помощь.