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 .
Увидеть как это работает
Выполните следующие действия, чтобы увидеть начальное состояние веб-сайта:
- Нажмите
Показать >
В новом окне , чтобы увидеть живой веб-сайт .
- Введите имя пользователя по вашему выбору и нажмите « Далее ».
- Введите пароль и нажмите Войти .
Пароль игнорируется, но вы все еще аутентифицированы. Вы попадаете на главную страницу.
- Щелкните Try reauth и повторите второй, третий и четвертый шаги.
- Щелкните Выйти .
Обратите внимание, что вы должны вводить пароль каждый раз, когда пытаетесь войти в систему. Это эмулирует пользователя, которому необходимо повторно пройти аутентификацию, прежде чем он сможет получить доступ к важному разделу веб-сайта.
Смешайте код
- Перейдите к WebAuthn/FIDO2 API Codelab .
- Нажмите на название вашего проекта > Remix Project .
чтобы разветвить проект и продолжить работу с собственной версией по новому URL-адресу.
3. Зарегистрируйте учетные данные с отпечатком пальца
Вам необходимо зарегистрировать учетные данные, сгенерированные UVPA, аутентификатором, встроенным в устройство и проверяющим личность пользователя. Обычно это рассматривается как датчик отпечатков пальцев в зависимости от устройства пользователя.
Вы добавляете эту функцию на /home
страницу:
Создать функцию 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
, поэтому он интуитивно понятен для вас. Следующая таблица содержит важные параметры, которые вы можете передать на сервер, и объясняет, что они делают:
Параметры | Описания | ||
| Предпочтение в отношении передачи аттестации — | ||
| Массив | ||
| | Фильтровать доступные аутентификаторы. Если вы хотите, чтобы к устройству был подключен аутентификатор, используйте « | |
| Определите, является ли проверка локального пользователя аутентификатора « | ||
| Используйте |
Чтобы узнать больше об этих опциях, см . 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" } }
Создать учетные данные
- Поскольку эти параметры доставляются закодированными для прохождения через протокол 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); } }
- Вызовите метод
navigator.credentials.create()
, чтобы создать новые учетные данные.
С помощью этого вызова браузер взаимодействует с аутентификатором и пытается проверить личность пользователя с помощью UVPA.
общедоступный/client.js
const cred = await navigator.credentials.create({ publicKey: options, });
Как только пользователь подтвердит свою личность, вы должны получить объект учетных данных, который вы можете отправить на сервер и зарегистрировать аутентификатор.
Зарегистрируйте учетные данные на конечной точке сервера
Вот пример объекта учетных данных, который вы должны были получить.
{ "id": "...", "rawId": "...", "type": "public-key", "response": { "clientDataJSON": "...", "attestationObject": "..." } }
- Как и в случае, когда вы получили объект опции для регистрации учетных данных, закодируйте двоичные параметры учетных данных, чтобы их можно было доставить на сервер в виде строки:
общедоступный/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, }; }
- Сохраните идентификатор учетных данных локально, чтобы вы могли использовать его для аутентификации, когда пользователь вернется:
общедоступный/client.js
localStorage.setItem(`credId`, credential.id);
- Отправьте объект на сервер и, если он вернет
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. Создайте пользовательский интерфейс для регистрации, получения и удаления учетных данных.
Хорошо иметь список зарегистрированных учетных данных и кнопки для их удаления.
Заполнитель пользовательского интерфейса сборки
Добавьте пользовательский интерфейс для списка учетных данных и кнопку для регистрации новых учетных данных. В зависимости от того, доступна функция или нет, вы удаляете 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:
- Изучите
window.PublicKeyCredential
, чтобы проверить, доступен ли WebAuthn. - Вызовите
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'); }
Получить и отобразить список учетных данных
- Создайте функцию
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); };
-
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)}`); };
- Добавьте
unregisterCredential
к существующему операторуimport
.
просмотры/home.html
import { _fetch, unregisterCredential } from '/client.js';
- Добавьте функцию, которая будет вызываться, когда пользователь нажимает кнопку « Удалить ».
просмотры/home.html
const removeCredential = async e => { try { await unregisterCredential(e.target.id); getCredentials(); } catch (e) { alert(e); } };
Зарегистрировать учетные данные
Вы можете вызвать registerCredential()
для регистрации новых учетных данных, когда пользователь нажимает Добавить учетные данные .
- Добавьте
registerCredential
к существующему операторуimport
.
просмотры/home.html
import { _fetch, registerCredential, unregisterCredential } from '/client.js';
- Вызвать
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
. Если биометрическая аутентификация недоступна или биометрическая аутентификация не удалась, пользовательский интерфейс возвращается к использованию существующей формы пароля.
Создать функцию authenticate()
Создайте функцию с именем authenticate()
, которая проверяет личность пользователя с помощью отпечатка пальца. Вы добавляете код JavaScript здесь:
общедоступный/client.js
export const authenticate = async () => { };
Получите вызов и другие параметры с конечной точки сервера.
- Перед аутентификацией проверьте, есть ли у пользователя сохраненный идентификатор учетных данных, и установите его в качестве параметра запроса, если он есть.
Когда вы указываете идентификатор учетных данных вместе с другими параметрами, сервер может предоставить соответствующие allowCredentials
и это делает проверку пользователя надежной.
общедоступный/client.js
const opts = {}; let url = '/auth/signinRequest'; const credId = localStorage.getItem(`credId`); if (credId) { url += `?credId=${encodeURIComponent(credId)}`; }
- Прежде чем просить пользователя пройти аутентификацию, попросите сервер отправить запрос и другие параметры. Вызовите
_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
должен быть либо отдельным объектом в массиве, либо пустым массивом в зависимости от того, найдены ли учетные данные с идентификатором в параметре запроса на стороне сервера.
- Разрешите обещание с
null
значением, когдаallowCredentials
является пустым массивом, чтобы пользовательский интерфейс возвращался к запросу пароля.
if (options.allowCredentials.length === 0) { console.info('No registered credentials found.'); return Promise.resolve(null); }
Локально проверьте пользователя и получите учетные данные
- Поскольку эти параметры доставляются закодированными для прохождения через протокол HTTP, преобразуйте некоторые параметры обратно в двоичные, в частности,
challenge
и экземплярыid
, включенные в массивallowCredentials
:
общедоступный/client.js
options.challenge = base64url.decode(options.challenge); for (let cred of options.allowCredentials) { cred.id = base64url.decode(cred.id); }
- Вызовите метод
navigator.credentials.get()
, чтобы проверить личность пользователя с помощью UVPA.
общедоступный/client.js
const cred = await navigator.credentials.get({ publicKey: options });
Как только пользователь подтвердит свою личность, вы должны получить объект учетных данных, который вы можете отправить на сервер и аутентифицировать пользователя.
Подтвердите учетные данные
Вот пример объекта PublicKeyCredential
( response
— AuthenticatorAssertionResponse
), который вы должны были получить:
{ "id": "...", "type": "public-key", "rawId": "...", "response": { "clientDataJSON": "...", "authenticatorData": "...", "signature": "...", "userHandle": "" } }
- Закодируйте двоичные параметры учетных данных, чтобы их можно было доставить на сервер в виде строки:
общедоступный/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, }; }
- Отправьте объект на сервер и, если он вернет
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. Включите повторную аутентификацию
Построить пользовательский интерфейс
Когда пользователь возвращается, вы хотите, чтобы он повторно аутентифицировался как можно проще и безопаснее. Вот где сияет биометрическая аутентификация. Однако есть случаи, когда биометрическая аутентификация может не работать:
- УФПА недоступен.
- Пользователь еще не зарегистрировал учетные данные на своем устройстве.
- Хранилище очищено, и устройство больше не помнит идентификатор учетных данных.
- Пользователь не может подтвердить свою личность по какой-либо причине, например, когда его палец мокрый или он носит маску.
Вот почему всегда важно предоставлять другие варианты входа в качестве запасных вариантов. В этой лаборатории кода вы используете решение для пароля на основе формы.
- Добавьте пользовательский интерфейс, чтобы показать кнопку аутентификации, которая вызывает биометрическую аутентификацию в дополнение к форме пароля.
Используйте 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>
- Добавьте
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'); });
Активировать биометрическую аутентификацию
Наконец, включите биометрическую аутентификацию.
- Добавьте
authenticate
к существующему операторуimport
:
просмотры/reauth.html
import { _fetch, authenticate } from '/client.js';
- Вызовите
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. Поздравляем!
Вы закончили эту лабораторную работу!
Учить больше
- Веб-аутентификация: API для доступа к учетным данным открытого ключа уровня 1
- Введение в WebAuthn API
- Семинар FIDO WebAuthn
- Руководство по WebAuthn: DUOSEC
- Ваш первый Android FIDO2 API
Отдельное спасибо Юрию Акерманну из FIDO Alliance за помощь.