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 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 w formularzach do sugerowania kluczy dostępu może ułatwić tworzenie jednolitego interfejsu.

Dlaczego warto używać Autouzupełniania formularzy do logowania się za pomocą klucza dostępu?

Dzięki kluczom dostępu użytkownik może logować się na stronie internetowej przy użyciu odcisku palca, skanu twarzy lub kodu PIN urządzenia.

W idealnym świecie użytkownicy nie musieliby używać haseł, 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 przejść weryfikację, 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 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:

  • Backend: serwer backendowy, na którym znajduje się baza danych kont zawierająca klucz publiczny i inne metadane dotyczące 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.
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 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.
  4. 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, a nie wypełniając formularza 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 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: 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: Wskazanie, 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.

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 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 na serwer RP.

Gdy użytkownik wybierze konto i wyraży zgodę, korzystając z 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: zwraca platform, gdy te dane logowania pochodzą z urządzenia lokalnego. W przeciwnym razie cross-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 na serwer całego obiektu 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.

Zasoby