Klucze dostępu sprawiają, że konta użytkowników są bezpieczniejsze, prostsze i łatwiejsze w użyciu.
Korzystanie z kluczy dostępu zwiększa bezpieczeństwo, upraszcza logowanie i zastępuje hasła. W przeciwieństwie do zwykłych haseł, które użytkownicy muszą pamiętać i wpisywać ręcznie, klucze dostępu korzystają z mechanizmów blokady ekranu urządzenia, takich jak dane biometryczne lub kody PIN, i zmniejszają ryzyko wyłudzenia informacji i kradzieży danych logowania.
Klucze dostępu są synchronizowane na urządzeniach za pomocą dostawców kluczy dostępu, takich jak Menedżer haseł Google czy Pęk kluczy iCloud.
Należy utworzyć klucz dostępu, który będzie bezpiecznie przechowywać klucz prywatny dostawcy kluczy dostępu wraz z niezbędnymi metadanymi, a klucz publiczny będzie przechowywany na Twoim serwerze do uwierzytelniania. Klucz prywatny generuje podpis po zweryfikowaniu użytkownika w prawidłowej domenie, co czyni klucze dostępu odpornymi na phishing. Klucz publiczny weryfikuje podpis, nie przechowując poufnych danych logowania, dzięki czemu klucze dostępu są odporne na kradzież danych logowania.
Jak działa tworzenie klucza dostępu
Zanim użytkownik będzie mógł zalogować się za pomocą klucza dostępu, musisz utworzyć ten klucz, powiązać go z kontem użytkownika i zapisać jego klucz publiczny na serwerze.
Możesz poprosić użytkowników o utworzenie klucza dostępu w jednym z tych przypadków:
- Podczas rejestracji lub po jej zakończeniu.
- Po zalogowaniu się.
- Po zalogowaniu się przy użyciu klucza dostępu z innego urządzenia (czyli
[authenticatorAttachment](https://web.dev/articles/passkey-form-autofill#authenticator-attachment)
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:
- Backend: przechowuje szczegóły konta użytkownika, w tym klucz publiczny.
- Frontend: komunikuje się z przeglądarką i pobiera z backendu niezbędne dane.
- Przeglądarka: uruchamia kod JavaScript i współdziała z interfejsem 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.

Zanim utworzysz klucz dostępu, sprawdź, czy system spełnia te wymagania wstępne:
Konto użytkownika jest weryfikowane za pomocą bezpiecznej metody (np. e-maila, weryfikacji telefonicznej lub federacji tożsamości) w krótkim czasie.
Frontend i backend mogą bezpiecznie komunikować się, aby wymieniać dane logowania.
Przeglądarka obsługuje WebAuthn i tworzenie kluczy.
W następnych sekcjach pokażemy, jak sprawdzić większość z nich.
Gdy system spełni te warunki, utworzy klucz dostępu w ten sposób:
- System uruchamia proces tworzenia klucza dostępu, gdy użytkownik podejmie odpowiednie działanie (np. kliknie przycisk „Utwórz klucz dostępu” na stronie zarządzania kluczami dostępu lub po zakończeniu rejestracji).
- Frontend wysyła do backendu żądanie niezbędnych danych uwierzytelniających, w tym informacji o użytkowniku, wyzwania i identyfikatorów danych uwierzytelniających, aby zapobiec duplikatom.
- Frontend wywołuje funkcję
navigator.credentials.create()
, aby poprosić dostawcę kluczy dostępu urządzenia o wygenerowanie klucza dostępu na podstawie informacji z back-endu. Pamiętaj, że to wywołanie zwraca obietnicę. - Urządzenie użytkownika uwierzytelnia użytkownika za pomocą metody biometrycznej, kodu PIN lub wzoru, aby utworzyć klucz dostępu.
- Dostawca klucza dostępu tworzy klucz dostępu i zwraca do interfejsu publiczny klucz zaufania, spełniając obietnicę.
- Frontend wysyła wygenerowane dane logowania z kluczem publicznym do backendu.
- Serwer przechowuje klucz publiczny i inne ważne dane na potrzeby przyszłej uwierzytelniania.
- Backend wysyła do użytkownika powiadomienie (np. e-maila) z prośbą o potwierdzenie utworzenia klucza dostępu i wykrywanie potencjalnego nieautoryzowanego dostępu.
Dzięki temu użytkownicy mogą rejestrować klucze dostępu w bezpieczny i płynny sposób.
Zgodność
Większość przeglądarek obsługuje WebAuthn, ale z pewnymi drobnymi wyjątkami. Szczegółowe informacje o zgodności przeglądarek i systemów operacyjnych znajdziesz na stronie passkeys.dev.
Tworzenie nowego klucza dostępu
Aby utworzyć nowy klucz dostępu, front-end powinien wykonać ten proces:
- Sprawdź zgodność.
- Pobiera informacje z backendu.
- Wywołaj interfejs WebAuth API, aby utworzyć klucz dostępu.
- Wyślij zwrócony klucz publiczny do backendu.
- Zapisz dane uwierzytelniające.
W sekcjach poniżej znajdziesz instrukcje.
Sprawdzanie zgodności
Przed wyświetleniem przycisku „Utwórz nowy klucz dostępu” interfejs powinien sprawdzić, czy:
- Przeglądarka obsługuje WebAuthn z
PublicKeyCredential
.
- Urządzenie obsługuje uwierzytelnianie na platformie (może tworzyć klucze dostępu i uwierzytelniać się za ich pomocą) za pomocą
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.
- Przeglądarka obsługuje warunkowy interfejs użytkownika WebAuthn z użyciem
PublicKeyCredenital.isConditionalMediationAvailable()
.
Ten fragment kodu pokazuje, jak sprawdzić zgodność przed wyświetleniem opcji związanych z kluczem dostępu.
// 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
}
});
}
W tym przykładzie przycisk Utwórz nowy klucz dostępu powinien być wyświetlany tylko wtedy, gdy są spełnione wszystkie warunki.
Pobieranie informacji z backendu
Gdy użytkownik kliknie przycisk, pobierz wymagane informacje z back-endu, aby wywołać funkcję navigator.credentials.create()
.
Ten fragment kodu przedstawia obiekt JSON z wymaganymi informacjami do wywołania funkcji navigator.credentials.create()
:
// Example `PublicKeyCredentialCreationOptions` contents
{
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,
}
}
Pary klucz-wartość w obiekcie zawierają te informacje:
challenge
: wyzwanie wygenerowane przez serwer w ArrayBuffer dla tej rejestracji.rp.id
: Identyfikator RP (Relying Party ID), domena i strona internetowa mogą określać domenę lub sufiks możliwy do zarejestrowania. Jeśli na przykład punkt początkowy 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 się uwierzytelnić wlogin.example.com
lub w dowolnej subdomenie domenyexample.com
. Więcej informacji znajdziesz w artykule Zezwalanie na ponowne użycie klucza dostępu w witrynach za pomocą żądań powiązanych źródeł.rp.name
: nazwa RP (strony korzystającej). Ta metoda została wycofana w ramach WebAuthn L3, ale została uwzględniona ze względu na zgodność.user.id
: niepowtarzalny identyfikator użytkownika w ArrayBuffer wygenerowany podczas tworzenia konta. Powinien być trwały, w przeciwieństwie do nazwy użytkownika, którą można edytować. Identyfikator użytkownika identyfikuje konto, ale nie powinien zawierać żadnych informacji umożliwiających identyfikację osoby. W systemie masz prawdopodobnie już identyfikator użytkownika, ale w razie potrzeby utwórz jeden specjalnie dla kluczy dostępu, aby nie zawierał on żadnych informacji umożliwiających identyfikację osoby.user.name
: 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 selektorze kont.user.displayName
: wymagana, 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. W zależności od przeglądarki może się on wyświetlać w sekcji selektora konta.pubKeyCredParams
: określa algorytmy klucza publicznego obsługiwane przez RP (usługodawcę). Zalecamy ustawienie wartości[{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]
. Określa ona obsługę ECDSA z P-256 i RSA PKCS#1. Obsługa tych algorytmów zapewnia pełne pokrycie.excludeCredentials
: lista już zarejestrowanych identyfikatorów danych logowania. Zapobiega rejestrowaniu tego samego urządzenia dwukrotnie, podając listę już zarejestrowanych identyfikatorów danych logowania. Elementtransports
, jeśli jest podany, powinien zawierać wynik wywołania funkcjigetTransports()
podczas rejestracji każdego elementu danych logowania.authenticatorSelection.authenticatorAttachment
: ustaw tę opcję na"platform"
wraz z opcjąhint: ['client-device']
, jeśli utworzenie klucza dostępu jest uaktualnieniem z hasła, na przykład w ramach promocji po zalogowaniu."platform"
wskazuje, że RP chce uwierzytelniania na platformie (uwierzytelniania wbudowanego w urządzenie na platformie), które nie wyświetla prośby o włożenie klucza bezpieczeństwa USB. Użytkownik ma prostszą opcję tworzenia klucza dostępu.authenticatorSelection.requireResidentKey
: ustaw na wartość logicznątrue
. Dane logowania, które można znaleźć (klucz rezydencki) przechowują informacje o użytkowniku w kluczu dostępu i pozwalają użytkownikom wybrać konto podczas uwierzytelniania.
.authenticatorSelection.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ść.
Zalecamy tworzenie obiektu na serwerze, kodowanie ArrayBuffer za pomocą Base64URL i pobieranie go z front-endu. Dzięki temu możesz odkodować ładunek za pomocą funkcji PublicKeyCredential.parseCreationOptionsFromJSON()
i przekazać go bezpośrednio do funkcji navigator.credentials.create()
.
Poniższy fragment kodu pokazuje, jak pobierać i dekodować informacje potrzebne do utworzenia klucza dostępu.
// Fetch an encoded `PubicKeyCredentialCreationOptions` from the server.
const _options = await fetch('/webauthn/registerRequest');
// Deserialize and decode the `PublicKeyCredentialCreationOptions`.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseCreationOptionsFromJSON(decoded_options);
...
Wywołanie interfejsu WebAuthn API w celu utworzenia klucza dostępu
Aby utworzyć nowy klucz dostępu, zadzwoń pod numer navigator.credentials.create()
. Interfejs API zwraca obietnicę, czekając na interakcję użytkownika w ramach okna modalnego.
// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
publicKey: options
});
Prześlij zwrócone dane logowania z kluczem publicznym do backendu
Po zweryfikowaniu użytkownika za pomocą blokady ekranu urządzenia tworzony jest klucz dostępu, a obietnica jest rozwiązywana, zwracając obiekt PublicKeyCredential do interfejsu.
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 okna błędu. Strona nie powinna traktować tego jako błędu. Użytkownik chciał zarejestrować lokalne urządzenie i tak się stało.NotAllowedError
: operacja została anulowana przez użytkownika.AbortError
: operacja została przerwana.- 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 formacie 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 zapleczu.rawId
: identyfikator danych logowania w postaci tablicy ArrayBuffer.response.clientDataJSON
: dane klienta zakodowane w ArrayBuffer.response.attestationObject
: obiekt atesta kodowany za pomocą ArrayBuffer. Zawiera on ważne informacje, takie jak identyfikator RP, flagi i klucz publiczny.authenticatorAttachment
: zwraca"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"
.
Zakoduj obiekt za pomocą metody .toJSON()
, zserializuj go za pomocą funkcji JSON.stringify()
, a potem wyślij na serwer.
...
// 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/registerResponse', {
method: 'post',
credentials: 'same-origin',
body: result
});
...
Zapisywanie danych logowania
Po otrzymaniu danych logowania z kluczem publicznym na zapleczu zalecamy użycie biblioteki lub rozwiązania po stronie serwera zamiast pisania własnego kodu do przetwarzania danych logowania z kluczem publicznym.
Następnie możesz zapisać informacje pobrane z danych logowania w bazie danych na potrzeby przyszłego użycia.
Na tej liście znajdują się zalecane właściwości do zapisania:
- Identyfikator danych uwierzytelniających: identyfikator danych uwierzytelniających zwrócony z danymi uwierzytelniania przy użyciu klucza publicznego.
- Nazwa certyfikatu: nazwa certyfikatu. Nazwij je według nazwy dostawcy klucza dostępu, który je utworzył, którą można zidentyfikować na podstawie identyfikatora AAGUID.
- Identyfikator użytkownika: identyfikator użytkownika użyty do utworzenia klucza dostępu.
- Klucz publiczny: klucz publiczny zwrócony z poświadczeń klucza publicznego. Jest to wymagane do weryfikacji oświadczenia dotyczącego klucza dostępu.
- Data i godzina utworzenia: zapisz datę i godzinę utworzenia klucza dostępu. Jest to przydatne do identyfikacji klucza dostępu.
- Data i godzina ostatniego użycia: zawiera datę i godzinę ostatniego użycia klucza dostępu do zalogowania się przez użytkownika. Jest to przydatne, aby określić, które klucze dostępu użytkownik użył (lub nie użył).
- AAGUID: unikalny identyfikator dostawcy klucza dostępu.
- Flaga możliwości tworzenia kopii zapasowej: true, jeśli urządzenie kwalifikuje się do synchronizacji klucza dostępu. Te informacje pomagają użytkownikom na stronie zarządzania kluczami dostępu rozpoznawać klucze dostępu, które można zsynchronizować, oraz klucze dostępu powiązane z urządzeniem (nie można ich zsynchronizować).
Szczegółowe instrukcje znajdziesz w artykule Rejestracja klucza dostępu po stronie serwera.
sygnalizuje, że rejestracja się nie udała;
Jeśli rejestracja klucza dostępu nie powiedzie się, może to wprowadzić użytkownika w błąd. Jeśli klucz dostępu jest dostępny dla użytkownika w usługodawcy kluczy dostępu, ale powiązany z nim klucz publiczny nie jest przechowywany po stronie serwera, próby logowania się za pomocą klucza dostępu nigdy się nie udadzą, a rozwiązywanie problemów z tym związanych jest trudne. W takim przypadku poinformuj o tym użytkownika.
Aby temu zapobiec, możesz przekazać nieznany klucz dostępu dostawcy klucza dostępu za pomocą interfejsu Signal API.
Wywołując PublicKeyCredential.signalUnknownCredential()
z identyfikatorem RP i identyfikatorem danych logowania, RP może poinformować dostawcę kluczy dostępu, że określone dane logowania zostały usunięte lub nie istnieją. To dostawca klucza dostępu decyduje, jak postąpić z tym sygnałem, ale jeśli jest to obsługiwane, klucz dostępu powinien zostać usunięty.
// 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.
...
}
}
Więcej informacji o interfejsie Signal API znajdziesz w artykule Zgodność kluczy dostępu z danymi logowania na serwerze za pomocą interfejsu Signal API.
Wysyłanie powiadomienia do użytkownika
Wysyłanie powiadomienia (np. e-maila) po zarejestrowaniu klucza dostępu pomaga użytkownikom wykrywać nieautoryzowany dostęp do konta. Jeśli atakujący utworzy klucz dostępu bez wiedzy użytkownika, klucz ten będzie można wykorzystać w przyszłości, nawet po zmianie hasła. Powiadomienie ostrzega użytkownika i pomaga mu zapobiec takiej sytuacji.
Lista kontrolna
- Przed zezwoleniem użytkownikowi na utworzenie klucza dostępu zweryfikuj go (najlepiej za pomocą poczty e-mail lub bezpiecznej metody).
- Uniemożliwia tworzenie duplikatów kluczy dostępu od tego samego dostawcy za pomocą funkcji
excludeCredentials
. - Zapisz identyfikator AAGUID, aby zidentyfikować dostawcę klucza dostępu i nazwać dane logowania dla użytkownika.
- sygnalizuje, że próba rejestracji klucza dostępu zakończyła się niepowodzeniem z wartością
PublicKeyCredential.signalUnknownCredential()
. - wysyłać powiadomienie do użytkownika po utworzeniu i zarejestrowaniu klucza dostępu na jego koncie;
Zasoby
- Rejestracja klucza dostępu po stronie serwera
- Dokument Apple: uwierzytelnianie użytkownika za pomocą usługi internetowej
- Dokument Google: logowanie bez hasła za pomocą kluczy dostępu
Następny krok: zaloguj się za pomocą klucza dostępu, korzystając z autouzupełniania formularzy.