Logowanie się za pomocą klucza dostępu przez autouzupełnianie formularzy

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 sprawiają, że konta użytkowników w internecie są bezpieczniejsze, prostsze i łatwiejsze w użyciu. Przejście z uwierzytelniania opartego na haśle na klucz dostępu może jednak skomplikować wrażenia użytkowników. Użycie autouzupełniania formularzy do sugerowania kluczy dostępu może pomóc w stworzeniu ujednoliconego systemu.

Dlaczego warto korzystać z autouzupełniania formularzy w celu 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 nie powinno być żadnych użytkowników haseł, a proces uwierzytelniania byłby tak prosty, jak przycisk 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 byłoby niekorzystne dla UX.

Klucze dostępu to również nowa technologia. Wyjaśnienie ich i zapewnienie, że użytkownicy czują się komfortowo, może być trudne dla witryn. Oba te problemy można rozwiązać, korzystając z dobrze znanych rozwiązań za pomocą autouzupełniania.

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 stanowi część standardu WebAuthn.

Gdy tylko użytkownik kliknie pole do wpisania nazwy użytkownika, pojawi się okno z sugestiami autouzupełniania, w którym podświetlone są zapisane klucze dostępu i sugestie 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:

  • Backend: serwer backendu, na którym znajduje się baza danych kont, w której jest przechowywany klucz publiczny i inne metadane klucza.
  • 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.
Schemat uwierzytelniania za pomocą klucza dostępu
  1. 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.
  2. 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.
  3. Gdy użytkownik zweryfikuje swoją tożsamość za pomocą blokady ekranu urządzenia, obietnica zostanie zrealizowana, a frontend zwrócono dane logowania klucza publicznego.
  4. Frontend wysyła do backendu dane logowania klucza publicznego. 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, a nie wypełniając formularza logowania. W takim przypadku odpowiedzialność za zalogowanie użytkownika spoczywa na stronie.

Dodaj adnotację do pola do wprowadzania danych w formularzu

W razie potrzeby dodaj atrybut autocomplete do pola nazwa użytkownika input. Dodaj tokeny usernamewebauthn, 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.

Obsługa przeglądarek

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Źródło

Obsługa przeglądarek

  • Chrome: 108.
  • Edge: 108.
  • Firefox: 119.
  • Safari: 16.

Źródło

// 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: wygenerowane przez serwer wyzwanie zabezpieczające w elemencie ArrayBuffer. Jest to wymagane, aby zapobiec atakom metodą powtórzenia. 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ż pustą tablica, aby użytkownik mógł wybrać dostępny klucz dostępu z listy wyświetlanej przez przeglądarkę.
  • userVerification: wskazuje, czy weryfikacja użytkownika za pomocą blokady ekranu urządzenia to "required", "preferred" czy "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.

Zadzwoń pod numer 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 identyfikatorem rp.id użytym podczas tworzenia klucza dostępu.

Pamiętaj, aby określić mediation: 'conditional', aby żądanie było warunkowe.

Wyślij zwrócone dane uwierzytelniające klucza publicznego do serwera RP

Gdy użytkownik wybierze konto i wyrazi zgodę przy użyciu blokady ekranu urządzenia, obietnica zostanie zrealizowana przez zwrócenie obiektu PublicKeyCredential do frontendu RP.

Obietnice mogą zostać odrzucone z różnych powodów. Błędy te zależą od właściwości name obiektu Error:

  • 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 logowania klucza publicznego zawiera te właściwości:

  • id: identyfikator zaszyfrowany w formacie base64url uwierzytelnionego klucza dostępu.
  • rawId: wersja identyfikatora danych logowania w formacie 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: obiekt ArrayBuffer danych uwierzytelniających. 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: zwraca wartość platform, jeśli 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 w celu przetworzenia obiektu.

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.

Zasoby