Utwórz funkcję logowania, która wykorzystuje klucze dostępu, ale nadal obsługuje użytkowników, którzy korzystają z haseł.
Z tego przewodnika dowiesz się, jak używać autouzupełniania formularzy, aby umożliwić użytkownikom logowanie się za pomocą kluczy dostępu i haseł. Automatyczne wypełnianie formularzy zapewnia spójne logowanie, co ułatwia przejście z haseł na bezpieczniejszą i przyjazną dla użytkownika metodę uwierzytelniania za pomocą klucza dostępu.
Dowiedz się, jak wdrożyć interfejs warunkowy WebAuthn, aby obsługiwać użytkowników kluczy dostępu i haseł przy minimalnym tarciu w dotychczasowych formularzach logowania.
Dlaczego warto używać autouzupełniania formularzy do logowania się za pomocą klucza dostępu?
Klucze dostępu umożliwiają użytkownikom logowanie się w witrynach za pomocą odcisku palca, rozpoznawania twarzy lub kodu PIN urządzenia.
Gdyby wszyscy użytkownicy mieli klucze dostępu, proces uwierzytelniania mógłby sprowadzać się do jednego przycisku logowania jednokrotnego. Kliknięcie tego przycisku umożliwi użytkownikowi bezpośrednią weryfikację konta za pomocą blokady ekranu i zalogowanie się.
Przejście z haseł na klucze dostępu wiąże się jednak z pewnymi wyzwaniami. W tym okresie witryny muszą obsługiwać zarówno użytkowników haseł, jak i kluczy dostępu. Oczekiwanie, że użytkownicy będą pamiętać, które witryny używają kluczy dostępu, i proszenie ich o wybór metody logowania z góry, pogarsza komfort korzystania z usługi.
Klucze dostępu to nowa technologia, a ich jasne wyjaśnienie może być trudne. Korzystanie ze znanego interfejsu autouzupełniania pomaga rozwiązać zarówno problem z przejściem na nowy system, jak i z koniecznością zapoznania się z nim przez użytkowników.
Korzystanie z interfejsu warunkowego
Aby skutecznie obsługiwać użytkowników korzystających z kluczy dostępu i haseł, uwzględnij klucze dostępu w sugestiach automatycznego wypełniania formularza. To podejście wykorzystuje warunkowy interfejs, funkcję standardu WebAuthn.
Gdy użytkownik skupi się na polu do wprowadzania nazwy użytkownika, pojawi się okno autouzupełniania, w którym będą widoczne zapisane klucze dostępu i hasła. Użytkownik może wybrać klucz dostępu lub hasło i zalogować się, używając blokady ekranu urządzenia, jeśli wybierze klucz dostępu.
Dzięki temu użytkownicy mogą logować się w Twojej witrynie za pomocą dotychczasowego formularza logowania, ale z dodatkową korzyścią w postaci kluczy dostępu, jeśli je mają.
Jak działa uwierzytelnianie za pomocą klucza dostępu
Do uwierzytelniania za pomocą klucza dostępu używasz interfejsu WebAuthn API.
Proces uwierzytelniania za pomocą klucza dostępu składa się z 4 elementów:
- Backend: przechowuje szczegóły konta użytkownika, w tym klucz publiczny.
- Frontend: komunikuje się z przeglądarką i pobiera niezbędne dane z backendu.
- Przeglądarka: uruchamia kod JavaScript i wchodzi w interakcję z WebAuthn API.
- Dostawca kluczy dostępu: tworzy i przechowuje klucz dostępu. Zwykle jest to menedżer haseł, np. Menedżer haseł Google, lub klucz bezpieczeństwa.
Proces uwierzytelniania za pomocą kluczy dostępu przebiega w ten sposób:
- Użytkownik wyświetla stronę logowania, a frontend wysyła do backendu żądanie wyzwania uwierzytelniania.
- Backend generuje i zwraca test WebAuthn powiązany z kontem użytkownika.
- Interfejs wywołuje
navigator.credentials.get()z wyzwaniem, aby zainicjować uwierzytelnianie za pomocą przeglądarki. - Przeglądarka, wchodząc w interakcję z dostawcą kluczy dostępu, prosi użytkownika o wybranie klucza dostępu (często za pomocą okna autouzupełniania wywoływanego przez ustawienie fokusu na polu logowania) i potwierdzenie tożsamości za pomocą blokady ekranu urządzenia lub danych biometrycznych.
- Po pomyślnej weryfikacji użytkownika dostawca klucza dostępu podpisuje challenge, a przeglądarka zwraca wynikowe dane logowania za pomocą klucza publicznego (w tym podpis) do interfejsu.
- Frontend wysyła te dane logowania do backendu.
- Backend weryfikuje podpis poświadczenia na podstawie zapisanego klucza publicznego użytkownika. Jeśli weryfikacja się powiedzie, backend loguje użytkownika.
Uwierzytelnianie za pomocą klucza dostępu przez autouzupełnianie formularza
Aby zainicjować uwierzytelnianie za pomocą klucza dostępu przy użyciu autouzupełniania formularza, po wczytaniu strony logowania wykonaj wywołanie warunkowe WebAuthnget. To wywołanie do navigator.credentials.get() zawiera opcję mediation: 'conditional'.
Żądanie warunkowe do interfejsu WebAuthn's
navigator.credentials.get() API nie wyświetla od razu interfejsu. Zamiast tego czeka w stanie oczekiwania, aż użytkownik wejdzie w interakcję z promptem autouzupełniania w polu nazwy użytkownika. Jeśli użytkownik wybierze klucz dostępu, przeglądarka rozwiąże oczekujący obiekt typu Promise za pomocą danych logowania, aby zalogować użytkownika, pomijając tradycyjne przesyłanie formularza. Jeśli użytkownik wybierze hasło, obietnica nie zostanie spełniona, a standardowy proces logowania za pomocą hasła będzie kontynuowany.rm. Za zalogowanie użytkownika odpowiada wtedy strona.
Dodawanie adnotacji do pola do wprowadzania danych formularza
Aby włączyć autouzupełnianie klucza dostępu, dodaj atrybut autocomplete do pola nazwy użytkownika input w formularzu. Uwzględnij zarówno username, jak i webauthn jako wartości rozdzielone spacją.
<input type="text" name="username" autocomplete="username webauthn" autofocus>
Dodanie znaku autofocus do tego pola automatycznie wywoła monit o autouzupełnianie podczas wczytywania strony, natychmiast wyświetlając dostępne hasła i klucze dostępu.
Wykrywanie cech
Przed wywołaniem warunkowego wywołania interfejsu WebAuthn API sprawdź, czy:
- Przeglądarka obsługuje WebAuthn z
PublicKeyCredential.
- Przeglądarka obsługuje wykrywanie funkcji za pomocą elementu
PublicKeyCredential.getClientCapabilities().
- Przeglądarka obsługuje interfejs warunkowy WebAuthn z
conditionalGet.
Poniższy fragment kodu pokazuje, jak sprawdzić, czy przeglądarka obsługuje te funkcje:
if (window.PublicKeyCredential && PublicKeyCredential.getClientCapabilities) {
const capabilities = await PublicKeyCredential.getClientCapabilities();
// Check if conditional mediation is available.
if (capabilities.conditionalGet === true) {
// The browser supports conditional mediation.
}
}
Pobieranie informacji z backendu
Backend musi udostępniać frontendowi kilka opcji inicjowania wywołania navigator.credentials.get(). Te opcje są zwykle pobierane jako obiekt JSON z punktu końcowego na serwerze.
Kluczowe właściwości w obiekcie options to:
challenge: wygenerowany przez serwer ciąg w formacie ArrayBuffer (zwykle zakodowany w formacie Base64URL na potrzeby przesyłania w formacie JSON). Jest to niezbędne, aby zapobiegać atakom typu replay. Serwer musi generować nowy test zabezpieczający przy każdej próbie logowania i unieważniać go po krótkim czasie lub w przypadku nieudanej próby.allowCredentials: tablica deskryptorów danych logowania. Przekaż pustą tablicę. Spowoduje to wyświetlenie w przeglądarce wszystkich danych logowania dla określonegorpId.userVerification: określa preferencje dotyczące weryfikacji użytkownika, np. wymaganie blokady ekranu urządzenia. Wartość domyślna i zalecana to"preferred". Możliwe wartości:"required": weryfikacja użytkownika musi być przeprowadzona przez uwierzytelnianie (np. kod PIN lub dane biometryczne). Operacja nie powiedzie się, jeśli nie można przeprowadzić weryfikacji."preferred": uwierzytelniacz próbuje zweryfikować użytkownika, ale operacja może się powieść bez tego."discouraged": uwierzytelnianie powinno w miarę możliwości unikać weryfikacji użytkownika.
rpId: identyfikator podmiotu ufającego, zwykle domena witryny (np.example.com). Ta wartość musi dokładnie odpowiadać wartościrp.idużytej podczas tworzenia danych logowania klucza dostępu.
Ten obiekt opcji powinien zostać utworzony przez serwer. Wartości ArrayBuffer (np. challenge) muszą być zakodowane w formacie Base64URL na potrzeby transportu JSON. Po stronie klienta po przeanalizowaniu kodu JSON użyj funkcji PublicKeyCredential.parseRequestOptionsFromJSON(), aby przekonwertować obiekt (w tym dekodowanie ciągów Base64URL) na format, którego oczekuje funkcja navigator.credentials.get().
Poniższy fragment kodu pokazuje, jak pobrać i zdekodować informacje potrzebne do uwierzytelnienia za pomocą klucza dostępu.
// Fetch an encoded PubicKeyCredentialRequestOptions from the server.
const _options = await fetch('/webauthn/signinRequest');
// Deserialize and decode the PublicKeyCredentialRequestOptions.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseRequestOptionsFromJSON(decoded_options);
...
Wywołaj interfejs WebAuthn API z flagą conditional, aby uwierzytelnić użytkownika.
Gdy przygotujesz obiekt publicKeyCredentialRequestOptions (w przykładowym kodzie poniżej oznaczony jako options), wywołaj funkcję navigator.credentials.get(), aby zainicjować uwierzytelnianie warunkowe za pomocą klucza dostępu.
// To abort a WebAuthn call, instantiate an AbortController.
const abortController = new AbortController();
// Invoke WebAuthn to authenticate with a passkey.
const credential = await navigator.credentials.get({
publicKey: options,
signal: abortController.signal,
// Specify 'conditional' to activate conditional UI
mediation: 'conditional'
});
Kluczowe parametry tego wywołania:
publicKey: musi to być obiektpublicKeyCredentialRequestOptions(w przykładzie o nazwieoptions) pobrany z serwera i przetworzony w poprzednim kroku.signal: Przekazanie sygnałuAbortController(np.abortController.signal) umożliwia programowe anulowanie żądaniaget(). Jest to przydatne, gdy chcesz wywołać inne połączenie WebAuthn.mediation: 'conditional': to kluczowa flaga, która sprawia, że wywołanie WebAuthn jest warunkowe. Informuje przeglądarkę, aby poczekała na interakcję użytkownika z promptem autouzupełniania, zamiast od razu wyświetlać okno modalne.
Prześlij zwrócone dane logowania klucza publicznego na serwer RP.
Jeśli użytkownik wybierze klucz dostępu i potwierdzi swoją tożsamość (np. za pomocą blokady ekranu urządzenia), obietnica navigator.credentials.get() zostanie spełniona. Zwraca to obiekt PublicKeyCredential do interfejsu.
Obietnica może zostać odrzucona z kilku powodów. W kodzie należy obsługiwać te błędy, sprawdzając właściwość name obiektu Error:
NotAllowedError: użytkownik anulował operację lub nie wybrano klucza dostępu.AbortError: operacja została przerwana, prawdopodobnie przez Twój kod za pomocą elementuAbortController.- Inne wyjątki: wystąpił nieoczekiwany błąd. Przeglądarka zwykle wyświetla użytkownikowi okno z błędem.
Obiekt PublicKeyCredential zawiera kilka właściwości. Kluczowe właściwości istotne dla uwierzytelniania to:
id: zakodowany w formacie base64url identyfikator uwierzytelnionego klucza dostępu.rawId: wersja identyfikatora danych logowania w formacie ArrayBuffer.response.clientDataJSON: ArrayBuffer z danymi klienta. To pole zawiera informacje takie jak wyzwanie i źródło, które serwer musi zweryfikować.response.authenticatorData: bufor ArrayBuffer z danymi uwierzytelniania. To pole zawiera informacje takie jak identyfikator RP.response.signature: ArrayBuffer zawierający podpis. Ta wartość jest podstawą danych logowania, a serwer musi weryfikować ten podpis za pomocą przechowywanego klucza publicznego powiązanego z danymi logowania .response.userHandle: ArrayBuffer zawierający identyfikator użytkownika podany podczas rejestracji klucza dostępu.authenticatorAttachment: wskazuje, czy uwierzytelnianie odbywa się na urządzeniu klienta (platform) czy na urządzeniu zewnętrznym (cross-platform). Załącznikcross-platformmoże wystąpić, jeśli użytkownik zalogował się za pomocą telefonu. W takich przypadkach możesz poprosić użytkownika o utworzenie klucza dostępu na bieżącym urządzeniu, aby ułatwić mu logowanie w przyszłości.type: to pole ma zawsze wartość"public-key".
Aby wysłać ten obiekt PublicKeyCredential do backendu, najpierw wywołaj metodę .toJSON(). Ta metoda tworzy wersję danych logowania, którą można serializować do formatu JSON. Prawidłowo obsługuje ona konwersję właściwości ArrayBuffer (takich jak rawId, clientDataJSON, authenticatorData, signature i userHandle) na ciągi znaków zakodowane w Base64URL. Następnie użyj funkcji JSON.stringify(), aby przekształcić ten obiekt w ciąg znaków i wysłać go w treści żądania do serwera.
...
// Encode and serialize the PublicKeyCredential.
const _result = credential.toJSON();
const result = JSON.stringify(_result);
// Encode and send the credential to the server for verification.
const response = await fetch('/webauthn/signinResponse', {
method: 'post',
credentials: 'same-origin',
body: result
});
Weryfikacja podpisu
Gdy serwer backendu otrzyma dane logowania z kluczem publicznym, musi zweryfikować ich autentyczność. Obejmuje to:
- Analizowanie danych logowania.
- Wyszukiwanie zapisanego klucza publicznego powiązanego z
id. - weryfikacja otrzymanego
signatureza pomocą zapisanego klucza publicznego. - weryfikowanie innych danych, takich jak wyzwanie i pochodzenie;
Zalecamy używanie biblioteki FIDO/WebAuthn po stronie serwera, aby bezpiecznie obsługiwać te operacje kryptograficzne. Biblioteki open source znajdziesz w repozytorium GitHub awesome-webauthn.
Jeśli podpis i wszystkie inne potwierdzenia są prawidłowe, serwer może zalogować użytkownika. Szczegółowe instrukcje weryfikacji po stronie serwera znajdziesz w artykule Uwierzytelnianie za pomocą klucza dostępu po stronie serwera.
Sygnalizowanie, że na backendzie nie znaleziono pasujących danych logowania
Jeśli podczas logowania serwer backendu nie może znaleźć danych logowania z pasującym identyfikatorem, użytkownik mógł wcześniej usunąć ten klucz dostępu z serwera, ale nie z usługi dostawcy kluczy dostępu. Ta niezgodność może prowadzić do dezorientacji użytkownika, jeśli dostawca klucza dostępu nadal będzie sugerować klucz, który nie działa już w Twojej witrynie. Aby to poprawić, musisz wysłać do dostawcy klucza dostępu sygnał o usunięciu osieroconego klucza dostępu.
Możesz użyć metody PublicKeyCredential.signalUnknownCredential(), która jest częścią interfejsu Webauthn Signal API, aby poinformować dostawcę klucza dostępu, że określone dane logowania zostały usunięte lub nie istnieją. Wywołaj tę metodę statyczną po stronie klienta, jeśli serwer wskazuje (np. za pomocą określonego kodu stanu HTTP, takiego jak 404), że przedstawiony identyfikator danych logowania jest nieznany. Podaj identyfikator RP i identyfikator nieznanych danych logowania. Dostawca klucza dostępu, jeśli obsługuje ten sygnał, powinien usunąć klucz dostępu.
// Detect authentication failure due to lack of the credential
if (response.status === 404) {
// Feature detection
if (PublicKeyCredential.signalUnknownCredential) {
await PublicKeyCredential.signalUnknownCredential({
rpId: "example.com",
credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
});
} else {
// Encourage the user to delete the passkey from the password manager nevertheless.
...
}
}
Po uwierzytelnieniu
W zależności od tego, jak użytkownik się zalogował, proponujemy różne ścieżki postępowania.
Jeśli użytkownik zalogował się bez klucza dostępu
Jeśli użytkownik zalogował się w Twojej witrynie bez klucza dostępu, może to oznaczać, że nie ma zarejestrowanego klucza dostępu na tym koncie lub na obecnym urządzeniu. To dobra okazja, aby zachęcić użytkowników do utworzenia klucza dostępu. Rozważ następujące podejścia:
- Uaktualnianie haseł do kluczy dostępu: używaj tworzenia warunkowego, czyli funkcji WebAuthn, która umożliwia przeglądarce automatyczne utworzenie klucza dostępu dla użytkownika po pomyślnym zalogowaniu się za pomocą hasła. Może to znacznie zwiększyć popularność kluczy dostępu, ponieważ upraszcza proces ich tworzenia. Dowiedz się, jak to działa i jak to wdrożyć: Ułatwianie użytkownikom przejścia na klucze dostępu
- Ręczne wyświetlanie prośby o utworzenie klucza dostępu: zachęcaj użytkowników do tworzenia kluczy dostępu. Może to być skuteczne po zakończeniu przez użytkownika bardziej złożonego procesu logowania, np. uwierzytelniania wielopoziomowego. Unikaj jednak nadmiernej liczby promptów, które mogą przeszkadzać użytkownikom.
Aby dowiedzieć się, jak zachęcić użytkowników do tworzenia kluczy dostępu i poznać inne dobre praktyki, zapoznaj się z przykładami w artykule Informowanie użytkowników o kluczach dostępu.
Jeśli użytkownik zalogował się za pomocą klucza dostępu
Gdy użytkownik zaloguje się za pomocą klucza dostępu, możesz na kilka sposobów zwiększyć wygodę korzystania z usługi i zapewnić spójność konta.
Zachęcanie do utworzenia nowego klucza dostępu po uwierzytelnieniu na innym urządzeniu
Jeśli użytkownik zaloguje się za pomocą klucza dostępu przy użyciu mechanizmu na innym urządzeniu (np. skanując kod QR telefonem), użyty klucz dostępu może nie być przechowywany lokalnie na urządzeniu, na którym się loguje. Może się tak zdarzyć, gdy:
- mają klucz dostępu, ale u dostawcy kluczy dostępu, który nie obsługuje systemu operacyjnego ani przeglądarki używanej do logowania;
- Utracili dostęp do dostawcy kluczy dostępu na urządzeniu, na którym się logują, ale klucz dostępu jest nadal dostępny na innym urządzeniu.
W takiej sytuacji możesz poprosić użytkownika o utworzenie nowego klucza dostępu na bieżącym urządzeniu. Dzięki temu w przyszłości nie będą musieli powtarzać procesu logowania na różnych urządzeniach. Aby sprawdzić, czy użytkownik zalogował się za pomocą klucza dostępu na innym urządzeniu, sprawdź właściwość authenticatorAttachment danych logowania. Jeśli wartość to "cross-platform", oznacza to uwierzytelnianie na różnych urządzeniach. Jeśli tak, wyjaśnij, dlaczego warto utworzyć nowy klucz dostępu, i przeprowadź użytkownika przez proces tworzenia.
Synchronizowanie szczegółów klucza dostępu z dostawcą za pomocą sygnałów
Aby zapewnić spójność i większy komfort użytkownikom, strona ufająca może używać interfejsu WebAuthn Signals API do przekazywania dostawcy kluczy dostępu aktualizacji dotyczących danych logowania i informacji o użytkowniku.
Aby na przykład lista kluczy dostępu użytkownika u dostawcy kluczy dostępu była aktualna, synchronizuj dane logowania na backendzie. Możesz sygnalizować, że klucz dostępu już nie istnieje, aby dostawcy kluczy dostępu mogli usuwać niepotrzebne klucze.
Podobnie możesz sygnalizować, czy użytkownik zaktualizował nazwę użytkownika lub nazwę wyświetlaną w Twojej usłudze, aby pomóc w utrzymaniu aktualności informacji o użytkowniku wyświetlanych przez dostawcę klucza dostępu (np. w oknach wyboru konta).
Więcej informacji o sprawdzonych metodach zapewniania spójności kluczy dostępu znajdziesz w artykule Zapewnianie spójności kluczy dostępu z danymi logowania na serwerze za pomocą interfejsu Signal API.
Nie proś o drugi składnik
Klucze dostępu zapewniają solidną, wbudowaną ochronę przed typowymi zagrożeniami, takimi jak phishing. Dlatego drugi czynnik uwierzytelniania nie zwiększa znacząco bezpieczeństwa. Zamiast tego utrudnia logowanie użytkownikom.
Lista kontrolna
- Zezwalaj użytkownikom na logowanie się za pomocą klucza dostępu przez autouzupełnianie formularza.
- Sygnalizowanie, gdy na backendzie nie znaleziono pasujących danych logowania klucza dostępu.
- Po zalogowaniu się użytkownika wyświetl mu prośbę o ręczne utworzenie klucza dostępu, jeśli jeszcze go nie utworzył.
- Automatyczne tworzenie klucza dostępu (tworzenie warunkowe) po zalogowaniu się użytkownika za pomocą hasła (i drugiego czynnika).
- Wyświetlaj prośbę o utworzenie lokalnego klucza dostępu, jeśli użytkownik zalogował się za pomocą klucza dostępu na innym urządzeniu.
- Po zalogowaniu lub po wprowadzeniu zmian przesyłaj do dostawcy listę dostępnych kluczy dostępu i zaktualizowane dane użytkownika (nazwę użytkownika, nazwę wyświetlaną).
Zasoby
- Uwierzytelnianie za pomocą klucza dostępu po stronie serwera
- Tworzenie klucza dostępu do logowania bez hasła
- Pomaganie użytkownikom w skutecznym zarządzaniu kluczami dostępu
- Klucze dostępu
- Dokument Apple: Authenticating a User Through a Web Service (Uwierzytelnianie użytkownika za pomocą usługi internetowej)
- Dokument Google: logowanie bez hasła za pomocą kluczy dostępu