Klucze dostępu sprawiają, że konta użytkowników są bezpieczniejsze, prostsze i łatwiejsze w użyciu.
Korzystanie z kluczy dostępu zamiast haseł to świetny sposób na to, aby witryny były bezpieczniejsze, prostsze, łatwiejsze w użyciu i nie wymagały haseł. Dzięki kluczowi dostępu użytkownik może zalogować się na stronie lub w aplikacji za pomocą odcisku palca, wizerunku twarzy lub kodu PIN urządzenia.
Klucz dostępu musi zostać utworzony, powiązany z kontem użytkownika i związany z kluczem publicznym na serwerze, zanim użytkownik będzie mógł się zalogować.
Jak to działa
Użytkownik może zostać poproszony o utworzenie klucza dostępu w jednym z tych scenariuszy:
- Gdy użytkownik loguje się za pomocą hasła.
- Gdy użytkownik loguje się za pomocą klucza dostępu z innego urządzenia (czyli
authenticatorAttachment
jestcross-platform
). - Na specjalnej stronie, na której użytkownicy mogą zarządzać kluczami.
Aby utworzyć klucz dostępu, użyj interfejsu WebAuthn API.
Cztery elementy procesu rejestracji klucza:
- Back-end: serwer back-end, na którym znajduje się baza danych kont zawierająca klucz publiczny i inne metadane klucza dostępu.
- Frontend: frontend, 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 obejmować menedżera haseł na tym samym urządzeniu co przeglądarka (np. gdy używasz Windows Hello) lub na innym urządzeniu, takim jak telefon.
Proces dodawania nowego klucza dostępu do dotychczasowego konta użytkownika wygląda tak:
- Użytkownik loguje się w witrynie.
- Po zalogowaniu się użytkownik prosi o utworzenie klucza dostępu na interfejsie, na przykład przez naciśnięcie przycisku „Utwórz klucz dostępu”.
- Interfejs prosi o informacje z backendu w celu utworzenia klucza dostępu, takiego jak informacje o użytkowniku, test zabezpieczający i identyfikatory danych logowania do wykluczenia.
- Frontend wywołuje funkcję
navigator.credentials.create()
, aby utworzyć klucz dostępu. To wywołanie zwraca obietnicę. - Klucz dostępu jest tworzony po wyrażeniu przez użytkownika zgody za pomocą blokady ekranu. Obietnica została rozwiązana, a do frontendu zwrócone są dane uwierzytelniające klucza publicznego.
- Frontend wysyła dane logowania z kluczem publicznym do backendu i przechowuje identyfikator danych logowania oraz klucz publiczny powiązany z kontem użytkownika na potrzeby przyszłych uwierzytelnień.
Zgodność
WebAuthn jest obsługiwany przez większość przeglądarek, ale występują pewne niewielkie luki. Aby dowiedzieć się, jakie kombinacje przeglądarek i systemów operacyjnych obsługują tworzenie kluczy dostępu, odwiedź stronę Obsługa urządzeń – passkeys.dev.
Tworzenie nowego klucza dostępu
Oto jak frontend powinien działać po otrzymaniu żądania utworzenia nowego klucza dostępu.
Wykrywanie cech
Zanim wyświetlisz przycisk „Utwórz nowy klucz dostępu”, sprawdź, czy:
- Przeglądarka obsługuje WebAuthn z
PublicKeyCredential
.
- Urządzenie obsługuje usługę uwierzytelniającą platformy (może utworzyć klucz dostępu i uwierzytelnić się za pomocą klucza dostępu) za pomocą
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.
- Przeglądarka obsługuje warunkowy interfejs użytkownika WebAuthn z użyciem
PublicKeyCredenital.isConditionalMediationAvailable()
.
// Availability of `window.PublicKeyCredential` means WebAuthn is usable.
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.
// `isConditionalMediationAvailable` means the feature detection is usable.
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
// Check if user verifying platform authenticator is available.
Promise.all([
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
PublicKeyCredential.isConditionalMediationAvailable(),
]).then(results => {
if (results.every(r => r === true)) {
// Display "Create a new passkey" button
}
});
}
Dopóki nie zostaną spełnione wszystkie warunki, klucze dostępu nie będą obsługiwane w tej przeglądarce. Do tego czasu przycisk „Utwórz nowy klucz dostępu” nie powinien się wyświetlać.
Pobieranie ważnych informacji z back-endu
Gdy użytkownik kliknie przycisk, pobierz ważne informacje do wywołania funkcjinavigator.credentials.create()
z tego backendu:
challenge
: wyzwanie wygenerowane przez serwer w ArrayBuffer dla tej rejestracji. Jest on wymagany, ale nieużywany podczas rejestracji, chyba że przeprowadzasz weryfikację – zaawansowany temat, który nie jest tutaj omawiany.user.id
: unikalny identyfikator użytkownika. Ta wartość musi być tablicą ArrayBuffer, która nie zawiera informacji umożliwiających identyfikację, takich jak adresy e-mail czy nazwy użytkowników. Na potrzeby tego testu wystarczy losowa wartość 16-bajtowa wygenerowana dla każdego konta.user.name
: to pole powinno zawierać unikalny identyfikator konta, który użytkownik będzie w stanie rozpoznać, np. adres e-mail lub nazwę użytkownika. Będzie ona widoczna w selecterze kont. (W przypadku nazwy użytkownika użyj takiej samej wartości jak podczas uwierzytelniania hasła).user.displayName
: to pole jest wymagane. Jest to bardziej przyjazna dla użytkownika nazwa konta. Nie musi być niepowtarzalna i może być nazwą wybraną przez użytkownika. Jeśli Twoja witryna nie ma odpowiedniej wartości do uwzględnienia tutaj, prześlij pusty ciąg znaków. Ta informacja może się wyświetlać w selektorze kont w zależności od przeglądarki.excludeCredentials
: zapobiega rejestrowaniu tego samego urządzenia, podając listę już zarejestrowanych identyfikatorów danych logowania. Elementtransports
(jeśli został podany) powinien zawierać wynik wywołaniagetTransports()
podczas rejestracji poszczególnych danych logowania.
Wywołaj interfejs WebAuthn API, aby utworzyć klucz dostępu
Aby utworzyć nowy klucz dostępu, zadzwoń pod numer navigator.credentials.create()
. Interfejs API zwraca obietnicę, która czeka na interakcję użytkownika i wyświetla okno modalne.
const publicKeyCredentialCreationOptions = {
challenge: *****,
rp: {
name: "Example",
id: "example.com",
},
user: {
id: *****,
name: "john78",
displayName: "John",
},
pubKeyCredParams: [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}],
excludeCredentials: [{
id: *****,
type: 'public-key',
transports: ['internal'],
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
// Encode and send the credential to the server for verification.
Parametry, których nie wyjaśniono powyżej:
rp.id
: identyfikator RP to domena, a witryna może określić swoją domenę lub sufiks możliwy do zarejestrowania. Jeśli na przykład źródło RP tohttps://login.example.com:1337
, identyfikator RP może byćlogin.example.com
lubexample.com
. Jeśli identyfikator RP toexample.com
, użytkownik może uwierzytelnić się na domenielogin.example.com
lub na dowolnych subdomenach domenyexample.com
.rp.name
: nazwa RP.pubKeyCredParams
: to pole określa obsługiwane przez RP algorytmy kluczy publicznych. Zalecamy ustawienie jej na[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]
. Określa ona obsługę ECDSA z P-256 i RSA PKCS#1. Ich obsługa zapewnia pełne pokrycie.authenticatorSelection.authenticatorAttachment
: ustaw tę opcję na"platform"
, jeśli utworzenie klucza dostępu jest uaktualnieniem z hasła, np. w ramach promocji po zalogowaniu się."platform"
oznacza, że w RPA jest wymagany moduł uwierzytelniający platformy (umieszczony na platformie), który nie wyświetla prośby o podłączenie np. klucza bezpieczeństwa USB. Użytkownik ma prostszą opcję tworzenia klucza dostępu.authenticatorSelection.requireResidentKey
: ustaw tę opcję na wartość logiczną „true” (prawda). Dane logowania umożliwiające ich znalezienie (klucz rezydencki) przechowują informacje o użytkowniku w kluczu dostępu i pozwalają użytkownikom wybrać konto podczas uwierzytelniania. Więcej informacji o danych logowania, które można znaleźć, znajdziesz w artykule Dane logowania, które można znaleźć.authenticatorSelection.userVerification
: wskazuje, 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 uwierzytelnianie może pominąć weryfikację użytkownika. Ustaw tę wartość na"preferred"
lub pomiń tę właściwość.
Wyślij zwrócone dane logowania klucza publicznego do backendu
Gdy użytkownik wyrazi zgodę przy użyciu blokady ekranu urządzenia, tworzony jest klucz dostępu, a obietnica zostaje zrealizowana przez zwrócenie obiektu PublicKeyCredential do frontendu.
Obietnice mogą zostać odrzucone z różnych powodów. Te błędy możesz naprawić, sprawdzając właściwość name
obiektu Error
:
InvalidStateError
: klucz dostępu jest już na urządzeniu. Użytkownik nie zobaczy żadnego okna błędu, a witryna nie powinna traktować tego jako błędu – użytkownik chciał zarejestrować urządzenie lokalne i już to zrobił.NotAllowedError
: użytkownik anulował operację.- 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 utworzonego klucza dostępu zakodowany w Base64URL. Ten identyfikator pomaga przeglądarce określić, czy po uwierzytelnieniu na urządzeniu znajduje się pasujący klucz dostępu. Ta wartość musi być przechowywana w bazie danych na serwerze.rawId
: wersja identyfikatora danych logowania w formacie ArrayBuffer.response.clientDataJSON
: dane klienta zakodowane w formacie ArrayBuffer.response.attestationObject
: obiekt uwierzytelnienia zakodowany w ArrayBuffer. Zawiera on ważne informacje, takie jak identyfikator RP, flagi i klucz publiczny.authenticatorAttachment
: zwraca wartość"platform"
, gdy te dane uwierzytelniające są tworzone na urządzeniu obsługującym klucze dostępu.type
: to pole ma zawsze wartość"public-key"
.
Jeśli używasz biblioteki do obsługi obiektu danych logowania klucza publicznego w backendzie, zalecamy wysłanie całego obiektu do backendu po częściowym zakodowaniu go za pomocą base64url.
Zapisywanie danych logowania
Po otrzymaniu danych logowania na kluczu publicznym na zapleczu prześlij je do biblioteki FIDO w celu przetworzenia obiektu.
Następnie możesz zapisać informacje pobrane z danych logowania w bazie danych na przyszłość. Na liście poniżej znajdziesz typowe właściwości do zapisania:
- Identyfikator danych logowania (klucz podstawowy)
- User-ID
- Klucz publiczny
Dane logowania klucza publicznego zawierają też te informacje, które warto zapisać w bazie danych:
- Flaga dostępności kopii zapasowej:
true
jeśli urządzenie kwalifikuje się do synchronizacji klucza dostępu. - Flaga stanu kopii zapasowej:
true
jeśli utworzony klucz dostępu jest rzeczywiście ustawiony do synchronizacji. - Transporty:
Lista transportów obsługiwanych przez urządzenie:
"internal"
oznacza, że urządzenie obsługuje klucz dostępu,"hybrid"
oznacza, że urządzenie obsługuje też uwierzytelnianie na innym urządzeniu.
Szczegółowe instrukcje znajdziesz w artykule Rejestracja klucza dostępu po stronie serwera.
Aby uwierzytelnić użytkownika, przeczytaj artykuł Logowanie się za pomocą klucza dostępu przez autouzupełnianie formularzy.