1. Zanim zaczniesz
Web Authentication API, nazywany też WebAuthn, umożliwia tworzenie i używanie danych logowania klucza publicznego o zakresie na poziomie źródła do uwierzytelniania użytkowników.
Interfejs API umożliwia korzystanie z modułów uwierzytelniających BLE, NFC i U2F lub USB FIDO2 (zwanych też kluczami bezpieczeństwa), a także uwierzytelniania platformy, które umożliwia użytkownikom uwierzytelnianie odciskiem palca lub blokadą ekranu.
W ramach tego ćwiczenia utworzysz witrynę z prostą funkcją ponownego uwierzytelniania, która wykorzystuje czytnik linii papilarnych. Ponowne uwierzytelnianie chroni dane konta, ponieważ użytkownicy, którzy już zalogowali się w witrynie, muszą ponownie przeprowadzić uwierzytelnianie, gdy próbują wpisać ważne sekcje witryny lub ponownie odwiedzić witrynę po upływie określonego czasu.
Wymagania wstępne
- Podstawowe informacje o działaniu WebAuthn
- Podstawowe umiejętności programowania z użyciem JavaScriptu
Jakie zadania wykonasz:
- Tworzenie witryny za pomocą prostej funkcji ponownego uwierzytelniania, która wykorzystuje czytnik linii papilarnych
Czego potrzebujesz
- Jedno z tych urządzeń:
- Urządzenie z Androidem, najlepiej z czujnikiem biometrycznym
- iPhone'a lub iPada z Touch ID lub Face ID w systemie iOS 14 lub nowszym
- MacBook Pro lub Air z czytnikiem Touch ID w systemie macOS Big Sur lub nowszym
- Windows 10 19H1 lub nowszy z zainstalowaną usługą Windows Hello
- 1 z tych przeglądarek:
- Google Chrome w wersji 67 lub nowszej
- Microsoft Edge 85 lub nowsza wersja
- Safari w wersji 14 lub nowszej.
2. Konfiguracja
W tym ćwiczeniu wykorzystaliśmy usługę o nazwie glitch. Możesz tutaj edytować kod JavaScript po stronie klienta i serwera i od razu je wdrażać.
Wejdź na https://glitch.com/edit":{"/webauthn-codelab-start.
Zobacz, jak to działa
Aby sprawdzić początkowy stan witryny:
- Kliknij Pokaż > W nowym oknie, aby zobaczyć opublikowaną witrynę.
- Wpisz wybraną nazwę użytkownika i kliknij Dalej.
- Wpisz hasło i kliknij Zaloguj się.
Hasło jest ignorowane, ale Ty nadal jesteś uwierzytelniony. Jesteś na stronie głównej.
- Kliknij Wypróbuj ponowne uwierzytelnienie i powtórz drugi, trzeci i czwarty krok.
- Kliknij Wyloguj się.
Pamiętaj, że za każdym razem, gdy próbujesz się zalogować, musisz podać hasło. Emuluje on użytkownika, który musi ponownie się uwierzytelnić, zanim uzyska dostęp do ważnej sekcji witryny.
Remiksuj kod
- Otwórz WebAuthn / FIDO2 API Codelab.
- Kliknij nazwę projektu > Remix Project, aby utworzyć rozwidlenie projektu i kontynuować tworzenie własnej wersji pod nowym adresem URL.
3. Rejestrowanie danych logowania za pomocą odcisku palca
Musisz zarejestrować dane logowania wygenerowane przez UVPA, czyli wbudowaną funkcję uwierzytelniania, która weryfikuje tożsamość użytkownika. W zależności od urządzenia użytkownika zwykle jest to czytnik linii papilarnych.
Tę funkcję dodaj do strony /home
:
Utwórz funkcję registerCredential()
Utwórz funkcję registerCredential()
, która rejestruje nowe dane logowania.
publiczny/client.js
export const registerCredential = async () => {
};
Uzyskiwanie wyzwania i innych opcji z punktu końcowego serwera
Zanim poprosisz użytkownika o zarejestrowanie nowych danych logowania, musisz poprosić serwer o podanie parametrów zwracających WebAuthn, w tym testy zabezpieczające. Na szczęście masz już punkt końcowy serwera odpowiadający tym parametrom.
Dodaj ten kod do registerCredential()
.
publiczny/client.js
const opts = {
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
};
const options = await _fetch('/auth/registerRequest', opts);
Protokół między serwerem a klientem nie jest częścią specyfikacji WebAuthn. Te ćwiczenia z programowania są zgodne ze specyfikacją WebAuthn, a obiekt JSON przekazywany do serwera jest bardzo podobny do PublicKeyCredentialCreationOptions
, dzięki czemu jest intuicyjny. Poniższa tabela zawiera ważne parametry, które możesz przekazać do serwera, i wyjaśnia, do czego służą:
Parametry | Teksty reklam | ||
| Preferencja na atest – | ||
| Tablica | ||
|
| Filtruj dostępnych uwierzytelniających. Jeśli chcesz, aby do urządzenia dołączono aplikację uwierzytelniającą | |
| Ustal, czy weryfikacja lokalna użytkownika w aplikacji uwierzytelniania to " | ||
| Użyj wartości |
Więcej informacji o tych opcjach znajdziesz w sekcji 5.4. Opcje tworzenia danych logowania (słownik PublicKeyCredentialCreationOptions
).
Oto przykładowe opcje, które otrzymujesz z serwera.
{
"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"
}
}
Utwórz dane logowania
- Ponieważ te opcje są dostarczane zakodowane tak, aby przechodzą przez protokół HTTP, przekonwertuj niektóre parametry z powrotem na wartości binarne, a konkretnie ciągi
user.id
,challenge
i wystąpieniaid
w tablicyexcludeCredentials
:
publiczny/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);
}
}
- Wywołaj metodę
navigator.credentials.create()
, aby utworzyć nowe dane logowania.
Podczas tej rozmowy przeglądarka wchodzi w interakcję z uwierzytelnianiem i próbuje zweryfikować tożsamość użytkownika przy użyciu UVPA.
publiczny/client.js
const cred = await navigator.credentials.create({
publicKey: options,
});
Gdy użytkownik potwierdzi swoją tożsamość, otrzymasz obiekt danych uwierzytelniających, który możesz wysłać na serwer i zarejestrować moduł uwierzytelniający.
Zarejestruj dane logowania w punkcie końcowym serwera
Oto przykładowy obiekt danych logowania, który powinien był do Ciebie dotrzeć.
{
"id": "...",
"rawId": "...",
"type": "public-key",
"response": {
"clientDataJSON": "...",
"attestationObject": "..."
}
}
- Tak jak w przypadku otrzymania obiektu opcji do zarejestrowania danych logowania, zakoduj parametry binarne w danych logowania, aby można je było przesyłać na serwer w postaci ciągu znaków:
publiczny/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,
};
}
- Przechowuj lokalnie dane logowania, aby móc ich używać do uwierzytelniania, gdy użytkownik wróci:
publiczny/client.js
localStorage.setItem(`credId`, credential.id);
- Wyślij obiekt na serwer, a jeśli zwróci
HTTP code 200
, spróbuj zarejestrować nowe dane logowania jako zarejestrowane.
publiczny/client.js
return await _fetch('/auth/registerResponse' , credential);
Możesz teraz korzystać z pełnej funkcji registerCredential()
.
Ostatni kod sekcji
publiczny/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. Utwórz interfejs użytkownika, aby rejestrować, pobierać i usuwać dane logowania
Dobrze jest mieć listę zarejestrowanych danych logowania i przycisków do ich usuwania.
Obiekt zastępczy interfejsu użytkownika kompilacji
Dodaj interfejs użytkownika do listy danych logowania i przycisk do rejestracji nowych danych logowania. W zależności od tego, czy funkcja jest dostępna, możesz usunąć klasę hidden
z komunikatu ostrzegawczego lub przycisku rejestracji nowych danych logowania. ul#list
jest obiektem zastępczym dodawania listy zarejestrowanych danych logowania.
widoki/strona_glowna.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>
Wykrywanie funkcji i dostępność UVPA
Aby sprawdzić dostępność UVPA:
- Sprawdź
window.PublicKeyCredential
, aby sprawdzić, czy funkcja WebAuthn jest dostępna. - Zadzwoń na numer
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
, aby sprawdzić, czy jest dostępny kod UVPA . Jeśli są dostępne, pokazuje się przycisk umożliwiający zarejestrowanie nowych danych logowania. Jeśli któreś z nich nie jest dostępne, zostanie wyświetlone ostrzeżenie.
widoki/strona_glowna.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');
}
Pobieranie i wyświetlanie listy danych logowania
- Utwórz funkcję
getCredentials()
, aby otrzymywać zarejestrowane dane logowania i wyświetlać je na liście. Na szczęście masz już w serwerze/auth/getKeys
przydatny punkt końcowy, z którego możesz pobrać zarejestrowane dane logowania zalogowanego użytkownika.
Zwrócony plik JSON zawiera informacje logowania, takie jak id
i publicKey
. Możesz utworzyć kod HTML, aby wyświetlać je użytkownikom.
widoki/strona_glowna.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);
};
- Wywołaj
getCredentials()
, aby wyświetlić dane logowania, gdy użytkownik wejdzie na stronę/home
.
widoki/strona_glowna.html
getCredentials();
Usuń dane logowania
Na liście danych logowania dodano przycisk pozwalający usunąć poszczególne dane logowania. Aby usunąć te zapytania, możesz wysłać żądanie do /auth/removeKey
wraz z parametrem zapytania credId
.
publiczny/client.js
export const unregisterCredential = async (credId) => {
localStorage.removeItem('credId');
return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
- Dołącz
unregisterCredential
do istniejącej instrukcjiimport
.
widoki/strona_glowna.html
import { _fetch, unregisterCredential } from '/client.js';
- Dodaj funkcję wywoływaną po kliknięciu przez użytkownika Usuń.
widoki/strona_glowna.html
const removeCredential = async e => {
try {
await unregisterCredential(e.target.id);
getCredentials();
} catch (e) {
alert(e);
}
};
Rejestrowanie danych logowania
Możesz wywołać registerCredential()
, by zarejestrować nowe dane logowania, gdy użytkownik kliknie Dodaj dane logowania.
- Dołącz
registerCredential
do istniejącej instrukcjiimport
.
widoki/strona_glowna.html
import { _fetch, registerCredential, unregisterCredential } from '/client.js';
- Wywołaj
registerCredential()
z opcjami dlanavigator.credentials.create()
.
Nie zapomnij odnowić nazwy na koncie przez wywołanie getCredentials()
po zarejestrowaniu.
widoki/strona_glowna.html
register.addEventListener('click', e => {
registerCredential().then(user => {
getCredentials();
}).catch(e => alert(e));
});
Teraz powinno być możliwe zarejestrowanie nowych danych logowania i wyświetlanie informacji na ich temat. Możesz wypróbować tę funkcję na swojej stronie internetowej.
Ostatni kod sekcji
widoki/strona_glowna.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/lit-html@1.0.0/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>
...
publiczny/client.js
...
export const unregisterCredential = async (credId) => {
localStorage.removeItem('credId');
return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
...
5. Uwierzytelnianie użytkownika odciskiem palca
Masz teraz zarejestrowane dane logowania i możesz ich używać do uwierzytelniania użytkowników. Teraz możesz dodać do witryny funkcję ponownego uwierzytelniania. Oto interfejs użytkownika:
Jeśli użytkownik wejdzie na stronę /reauth
, zobaczy przycisk Uwierzytelnij, jeśli to możliwe. Uwierzytelnianie odciskiem palca (UVPA) rozpoczyna się, gdy użytkownik kliknie Uwierzytelnij, przejdzie uwierzytelnianie, a następnie otworzy się strona /home
. Jeśli uwierzytelnianie biometryczne jest niedostępne lub uwierzytelnianie biometryczne nie powiedzie się, w interfejsie użytkownika zostanie użyty istniejący formularz hasła.
Utwórz funkcję authenticate()
Utwórz funkcję o nazwie authenticate()
, która weryfikuje tożsamość użytkownika odciskiem palca. Kod JavaScript dodaj tutaj:
publiczny/client.js
export const authenticate = async () => {
};
Uzyskiwanie wyzwania i innych opcji z punktu końcowego serwera
- Przed uwierzytelnieniem sprawdź, czy użytkownik ma zapisany identyfikator danych logowania, i ustaw go jako parametr zapytania, jeśli ma takie uprawnienia.
Gdy podasz identyfikator danych logowania oraz inne opcje, serwer będzie mógł podać dla Ciebie odpowiednie dane allowCredentials
, dzięki czemu weryfikacja użytkownika będzie wiarygodna.
publiczny/client.js
const opts = {};
let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
url += `?credId=${encodeURIComponent(credId)}`;
}
- Zanim poprosisz użytkownika o uwierzytelnienie, poproś serwer o wysłanie wyzwania i innych parametrów. Wywołaj
_fetch()
z argumentemopts
, aby wysłać żądanie POST do serwera.
publiczny/client.js
const options = await _fetch(url, opts);
Oto przykłady opcji, które powinny się wyświetlić (jest zgodne z ustawieniem PublicKeyCredentialRequestOptions
).
{
"challenge": "...",
"timeout": 1800000,
"rpId": "webauthn-codelab.glitch.me",
"userVerification": "required",
"allowCredentials": [
{
"id": "...",
"type": "public-key",
"transports": [
"internal"
]
}
]
}
Najważniejsza opcja to allowCredentials
. Gdy otrzymujesz opcje z serwera, atrybut allowCredentials
powinien być pojedynczym obiektem w tablicy lub pustą tablicą w zależności od tego, czy dane logowania z identyfikatorem w parametrze zapytania są po stronie serwera.
- Rozwiązywanie problemu przy użyciu
null
, gdyallowCredentials
jest pusta tablicą, aby interfejs użytkownika spytał o hasło.
if (options.allowCredentials.length === 0) {
console.info('No registered credentials found.');
return Promise.resolve(null);
}
Lokalna weryfikacja użytkownika i uzyskanie danych logowania
- Ponieważ te opcje są dostarczane zakodowane tak, by przechodzić przez protokół HTTP, przekonwertuj niektóre parametry z powrotem na binarne, a zwłaszcza
challenge
i wystąpieniaid
zawarte w tablicyallowCredentials
:
publiczny/client.js
options.challenge = base64url.decode(options.challenge);
for (let cred of options.allowCredentials) {
cred.id = base64url.decode(cred.id);
}
- Wywołaj metodę
navigator.credentials.get()
, aby zweryfikować tożsamość użytkownika za pomocą UVPA.
publiczny/client.js
const cred = await navigator.credentials.get({
publicKey: options
});
Gdy użytkownik potwierdzi swoją tożsamość, powinien otrzymać obiekt danych uwierzytelniających, który można wysłać na serwer i w celu uwierzytelnienia użytkownika.
Weryfikowanie danych logowania
Oto przykład obiektu PublicKeyCredential
(response
to AuthenticatorAssertionResponse
), który powinien być:
{
"id": "...",
"type": "public-key",
"rawId": "...",
"response": {
"clientDataJSON": "...",
"authenticatorData": "...",
"signature": "...",
"userHandle": ""
}
}
- Zakoduj parametry binarne w danych logowania, aby można je było przesyłać na serwer w postaci ciągu znaków:
publiczny/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,
};
}
- Wyślij obiekt na serwer, a jeśli zwróci
HTTP code 200
, weź pod uwagę, że użytkownik został zalogowany.
publiczny/client.js
return await _fetch(`/auth/signinResponse`, credential);
Możesz teraz korzystać z pełnej funkcji authentication()
.
Ostatni kod sekcji
publiczny/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. Włącz ponowne uwierzytelnianie
Interfejs kompilacji
Gdy użytkownik wróci, użytkownik powinien zadbać o to, aby jak najszybciej i bezpiecznie ponownie się uwierzytelnił. To właśnie tam dostrzec uwierzytelnianie biometryczne. Istnieją jednak przypadki uwierzytelniania biometrycznego:
- UVPA jest niedostępny.
- Użytkownik nie zarejestrował jeszcze żadnych danych logowania na swoim urządzeniu.
- Pamięć jest wyczyszczona, a urządzenie nie pamięta już identyfikatora logowania.
- Użytkownik z jakiegoś powodu nie może zweryfikować swojej tożsamości, na przykład palec jest mokry lub nosi maskę.
Dlatego zawsze warto przedstawić inne opcje logowania jako kreacje zastępcze. W tym ćwiczeniu wykorzystaliśmy formularz z hasłami opartymi na formularzach.
- Dodaj interfejs użytkownika, aby oprócz formularza hasła wyświetlać przycisk uwierzytelniania, który wywołuje uwierzytelnianie biometryczne.
Użyj klasy hidden
, aby wyświetlić i ukryć jedną z nich w zależności od stanu użytkownika.
views/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>
- Dołącz
class="hidden"
do formularza:
views/reauth.html
<form id="form" method="POST" action="/auth/password" class="hidden">
Wykrywanie funkcji i dostępność UVPA
Użytkownicy muszą logować się przy użyciu hasła, jeśli spełniasz jeden z tych warunków:
- Uwierzytelnianie WebAuthn jest niedostępne.
- UVPA jest niedostępny.
- Nie można znaleźć identyfikatora danych logowania tego UVPA.
Selektywnie wyświetl lub ukryj przycisk uwierzytelniania:
views/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');
}
Formularz powrotu do hasła
Użytkownik powinien też mieć możliwość zalogowania się za pomocą hasła.
Pokaż formularz hasła i ukryj przycisk uwierzytelniania, gdy użytkownik kliknie Zaloguj się za pomocą hasła:
views/reauth.html
const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
form.classList.remove('hidden');
document
.querySelector('#uvpa_available')
.classList.add('hidden');
});
Wywołaj uwierzytelnianie biometryczne
Na koniec włącz uwierzytelnianie biometryczne.
- Dołącz
authenticate
do istniejącej instrukcjiimport
:
views/reauth.html
import { _fetch, authenticate } from '/client.js';
- Wywołaj
authenticate()
, gdy użytkownik kliknie Uwierzytelnij, aby rozpocząć uwierzytelnianie biometryczne.
Upewnij się, że nie udało się zakończyć uwierzytelniania biometrycznego.
views/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');
});
});
Ostatni kod sekcji
views/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/material-components-web@7.0.0/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. Gratulacje!
Ćwiczenia z programowania zostały ukończone.
Więcej informacji
- Uwierzytelnianie za pomocą interfejsu API: dostęp do danych logowania klucza publicznego na poziomie 1
- Wprowadzenie do interfejsu WebAuthn API
- Warsztaty WebAuthn dotyczące FIDO
- Przewodnik WebAuthn: DUOSEC
- Pierwszy interfejs API FIDO2 Androida
Specjalne podziękowania dla Yuriy Ackermann z FIDO Alliance za pomoc.