utworzyć funkcję logowania się, która wykorzystuje klucze dostępu, a jednocześnie uwzględnia użytkowników, którzy korzystają z haseł.
Klucze dostępu zastępują hasła i ułatwiają użytkownikom bezpieczne korzystanie z kont w internecie. Jednak przejście z uwierzytelniania na podstawie hasła na uwierzytelnianie na podstawie klucza dostępu może skomplikować korzystanie z urządzenia. Używanie autouzupełniania formularzy do sugerowania kluczy dostępu może pomóc w ujednoliceniu obsługi.
Dlaczego warto używać autouzupełniania formularzy do logowania się za pomocą klucza dostępu?
Za pomocą klucza dostępu użytkownik może logować się na stronie internetowej przy użyciu odcisku palca, skanu twarzy lub kodu PIN urządzenia.
W idealnej sytuacji użytkownicy nie musieliby podawać hasła, a proces uwierzytelniania mógłby być tak prosty jak kliknięcie przycisku logowania jednokrotnego. Gdy użytkownik kliknie przycisk, pojawi się okno wyboru konta, w którym będzie można wybrać konto, odblokować ekran, aby potwierdzić dane i zalogować się.
Przejście z hasła na uwierzytelnianie za pomocą klucza dostępu może być jednak trudne. Podczas gdy użytkownicy będą przechodzić na klucze dostępu, nadal będą istnieć osoby, które używają haseł. Strony internetowe będą musiały uwzględniać potrzeby obu typów użytkowników. Nie można oczekiwać, że użytkownicy będą pamiętać, w których witrynach używają kluczy dostępu. Dlatego żądanie od nich wyboru metody na samym początku nie jest dobrym rozwiązaniem.
Klucze dostępu to też nowa technologia. Wyjaśnienie ich i zapewnienie, że użytkownicy czują się komfortowo, może być trudne dla witryn. Aby rozwiązać oba te problemy, możemy polegać na znajomych użytkownikom funkcjach autouzupełniania haseł.
Warunkowy interfejs
Aby zapewnić użytkownikom korzystającym z kluczy dostępu i haseł wygodę, możesz uwzględniać klucze dostępu w sugestiach autouzupełniania. Jest to tzw. warunkowe UI i należy do standardu WebAuthn.
Gdy użytkownik kliknie pole nazwy użytkownika, pojawi się okno z sugestiami autouzupełniania, w którym wyróżnione są zapisane klucze dostępu wraz z sugestiami autouzupełniania haseł. Użytkownik może wybrać konto i zalogować się, używając blokady ekranu urządzenia.
Dzięki temu użytkownicy mogą logować się na Twojej stronie za pomocą dotychczasowego formularza tak, jakby nic się nie zmieniło, ale z dodatkowym zabezpieczeniem w postaci kluczy dostępu, jeśli je mają.
Jak to działa
Aby uwierzytelnić się za pomocą klucza dostępu, użyj interfejsu WebAuthn API.
Proces uwierzytelniania za pomocą klucza dostępu składa się z 4 elementów:
- Back-end: serwer back-end, na którym znajduje się baza danych kont zawierająca klucz publiczny i inne metadane klucza dostępu.
- Frontend: interfejs, który komunikuje się z przeglądarką i wysyła żądania pobierania do backendu.
- Przeglądarka: przeglądarka użytkownika, w której działa kod JavaScript.
- Authenticator: aplikacja uwierzytelniająca użytkownika, która tworzy i przechowuje klucz dostępu. Może to być to samo urządzenie, na którym jest przeglądarka (np.gdy używasz Windows Hello), lub inne urządzenie, np. telefon.
- Gdy użytkownik wejdzie na stronę frontendową, ta wysyła do backendu prośbę o wyzwanie do uwierzytelniania za pomocą klucza dostępu i wywołuje funkcję
navigator.credentials.get()
, aby zainicjować uwierzytelnianie za pomocą klucza dostępu. Zwraca to wartośćPromise
. - Gdy użytkownik wskaże kursorem pole logowania, przeglądarka wyświetli okno autouzupełniania hasła, w którym znajdują się klucze dostępu. Jeśli użytkownik wybierze klucz dostępu, wyświetli się okno uwierzytelniania.
- Gdy użytkownik potwierdzi swoją tożsamość, używając blokady ekranu urządzenia, obietnica zostaje spełniona i klucz publiczny z poświadczeń jest zwracany do interfejsu.
- Frontend wysyła do backendu klucz publiczny. Backend weryfikuje podpis za pomocą klucza publicznego dopasowanego konta w bazie danych. Jeśli się powiedzie, użytkownik zostanie zalogowany.
Uwierzytelnienie za pomocą klucza dostępu przez autouzupełnianie formularzy
Gdy użytkownik chce się zalogować, możesz wywołać warunek WebAuthn get
, aby wskazać, że klucze dostępu mogą być uwzględniane w sugestiach autouzupełniania. Warunkowe wywołanie interfejsu API WebAuthn navigator.credentials.get()
nie wyświetla interfejsu użytkownika i pozostanie w stanie oczekującym, dopóki użytkownik nie wybierze konta, na którym chce się zalogować, z proponowanych opcji autouzupełniania. Jeśli użytkownik wybierze klucz dostępu, przeglądarka zrealizuje obietnicę za pomocą danych logowania zamiast wypełniać formularz logowania. W takim przypadku odpowiedzialność za zalogowanie użytkownika spoczywa na stronie.
Dodawanie adnotacji do pól formularza
W razie potrzeby dodaj atrybut autocomplete
do pola nazwa użytkownika input
.
Dodaj tokeny username
i webauthn
, aby sugerować klucze dostępu.
<input type="text" name="username" autocomplete="username webauthn" ...>
Wykrywanie cech
Przed wywołaniem warunkowego wywołania interfejsu WebAuthn API sprawdź, czy:
- Przeglądarka obsługuje WebAuthn z
PublicKeyCredential
.
- Przeglądarka obsługuje warunkowy interfejs użytkownika WebAuthn z użyciem
PublicKeyCredential.isConditionalMediationAvailable()
.
// Availability of `window.PublicKeyCredential` means WebAuthn is usable.
if (window.PublicKeyCredential &&
PublicKeyCredential.isConditionalMediationAvailable) {
// Check if conditional mediation is available.
const isCMA = await PublicKeyCredential.isConditionalMediationAvailable();
if (isCMA) {
// Call WebAuthn authentication
}
}
Pobieranie wyzwania z serwera RP
Pobierz wyzwanie z serwera RP, które jest wymagane do wywołania:
navigator.credentials.get()
:
challenge
: wyzwanie wygenerowane przez serwer w ArrayBuffer. Jest to wymagane, aby zapobiec atakom typu replay. Pamiętaj, aby generować nowe wyzwania przy każdej próbie logowania i ignorować je po upływie określonego czasu lub po nieudanej próbie weryfikacji. Możesz traktować go jak token CSRF.allowCredentials
: tablica akceptowanych danych uwierzytelniających. Przekaż pusty tablicę, aby umożliwić użytkownikowi wybranie dostępnego klucza dostępu z listy wyświetlanej przez przeglądarkę.userVerification
: Wskaźnik, czy weryfikacja użytkownika za pomocą blokady ekranu urządzenia jest ustawiona na"required"
,"preferred"
lub"discouraged"
. Wartość domyślna to"preferred"
, co oznacza, że uwierzytelniacz może pominąć weryfikację użytkownika. Ustaw tę wartość na"preferred"
lub pomiń tę właściwość.
Wywołaj interfejs WebAuthn API z flagą conditional
, aby uwierzytelnić użytkownika.
Wywołaj funkcję navigator.credentials.get()
, aby rozpocząć oczekiwanie na uwierzytelnianie użytkownika.
// To abort a WebAuthn call, instantiate an `AbortController`.
const abortController = new AbortController();
const publicKeyCredentialRequestOptions = {
// Server generated challenge
challenge: ****,
// The same RP ID as used during registration
rpId: 'example.com',
};
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions,
signal: abortController.signal,
// Specify 'conditional' to activate conditional UI
mediation: 'conditional'
});
rpId
: identyfikator RP to domena, a strona internetowa może wskazywać albo swoją domenę, albo sufiks, który można zarejestrować. Ta wartość musi być zgodna z wartością rp.id użytą podczas tworzenia klucza dostępu.
Aby uczynić żądanie warunkowe, pamiętaj o określeniu wartości mediation: 'conditional'
.
Prześlij zwrócone przez niego poświadczenia tożsamości klucza publicznego do serwera RP.
Gdy użytkownik wybierze konto i wyraży zgodę za pomocą blokady ekranu urządzenia, obietnica zostanie spełniona i zwróci obiekt PublicKeyCredential
do interfejsu RP.
Obietnice mogą zostać odrzucone z różnych powodów. W zależności od właściwości name
obiektu Error
musisz odpowiednio obsługiwać błędy:
NotAllowedError
: operacja została anulowana przez użytkownika.- Inne wyjątki: wystąpił nieoczekiwany błąd. Przeglądarka wyświetla użytkownikowi okno błędu.
Obiekt danych uwierzytelniających klucz publiczny zawiera te właściwości:
id
: identyfikator zaszyfrowany w formacie base64url uwierzytelnionego klucza dostępu.rawId
: identyfikator danych logowania w formie tablicy ArrayBuffer.response.clientDataJSON
: ArrayBuffer danych klienta. To pole zawiera informacje takie jak wyzwanie i źródło, które serwer RP musi zweryfikować.response.authenticatorData
: ArrayBuffer danych uwierzytelniającego. To pole zawiera informacje takie jak identyfikator RP.response.signature
:ArrayBuffer podpisu. Ta wartość jest podstawą danych logowania i musi zostać zweryfikowana na serwerze.response.userHandle
: ArrayBuffer zawierający identyfikator użytkownika ustawiony w momencie utworzenia. Ta wartość może być używana zamiast identyfikatora danych logowania, jeśli serwer musi wybrać wartości identyfikatorów, których używa, lub jeśli backend chce uniknąć tworzenia indeksu na podstawie identyfikatorów danych logowania.authenticatorAttachment
: zwracaplatform
, gdy te dane logowania pochodzą z urządzenia lokalnego. W przeciwnym raziecross-platform
, zwłaszcza gdy użytkownik zalogował się za pomocą telefonu. Jeśli użytkownik musiał użyć telefonu, aby się zalogować, możesz poprosić go o utworzenie klucza dostępu na urządzeniu lokalnym.type
: to pole ma zawsze wartość"public-key"
.
Jeśli na serwerze RP używasz biblioteki do obsługi obiektu danych logowania z kluczem publicznym, zalecamy wysłanie całego obiektu na serwer po częściowym zakodowaniu go za pomocą funkcji base64url.
Weryfikacja podpisu
Gdy otrzymasz na serwerze klucz publiczny, przekaż go do biblioteki FIDO, aby przetworzyć obiekt.
Odszukaj pasujący identyfikator danych logowania za pomocą właściwości id
. (Jeśli chcesz określić konto użytkownika, użyj właściwości userHandle
, która jest identyfikatorem user.id
określonym podczas tworzenia danych logowania). Sprawdź, czy dane uwierzytelniające
signature
można zweryfikować za pomocą zapisanego klucza publicznego. Zamiast pisać własny kod, zalecamy użycie biblioteki po stronie serwera lub rozwiązania. Biblioteki open source znajdziesz w repozytorium GitHub awesome-webauth.
Gdy dane logowania zostaną zweryfikowane za pomocą pasującego klucza publicznego, zaloguj użytkownika.
Szczegółowe instrukcje znajdziesz w artykule Uwierzytelnianie za pomocą klucza po stronie serwera.