Tworzenie klucza dostępu do logowania się bez hasła

Klucze dostępu sprawiają, że konta użytkowników są bezpieczniejsze, prostsze i łatwiejsze w użyciu.

Data publikacji: 12 października 2022 r., ostatnia aktualizacja: 9 kwietnia 2026 r.

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ą zapamiętać i wpisywać ręcznie, klucze dostępu korzystają z mechanizmów blokady ekranu urządzenia, takich jak dane biometryczne lub kody PIN, i ograniczają ryzyko wyłudzania informacji oraz kradzieży danych logowania.

Klucze dostępu są synchronizowane między urządzeniami przy użyciu dostawców kluczy dostępu, takich jak Menedżer haseł Google i pęk kluczy iCloud.

Klucz dostępu musi zostać utworzony, a klucz prywatny musi być bezpiecznie przechowywany u dostawcy klucza dostępu wraz z niezbędnymi metadanymi. Klucz publiczny musi być przechowywany na serwerze na potrzeby uwierzytelniania. Klucz prywatny wydaje podpis po weryfikacji użytkownika w prawidłowej domenie, dzięki czemu klucze dostępu są odporne na phishing. Klucz publiczny weryfikuje podpis bez przechowywania 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 go utworzyć, powiązać z kontem użytkownika i zapisać jego klucz publiczny na serwerze.

Możesz poprosić użytkowników o utworzenie klucza dostępu w jednej z tych sytuacji:

  • Podczas rejestracji lub po niej.
  • Po zalogowaniu się.
  • Po zalogowaniu się przy użyciu klucza dostępu z innego urządzenia (czyli authenticatorAttachment jest cross-platform).
  • na specjalnej stronie, na której użytkownicy mogą zarządzać kluczami dostępu;

Do utworzenia klucza dostępu służy interfejs WebAuthn API.

Proces rejestracji klucza dostępu składa się z 4 komponentó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 tworzenia i rejestrowania klucza dostępu
Proces tworzenia i rejestrowania klucza dostępu.

Zanim utworzysz klucz dostępu, upewnij się, że system spełnia te wymagania wstępne:

  • Konto użytkownika jest weryfikowane bezpieczną metodą (np. e-mailem, weryfikacją telefoniczną lub federacją tożsamości) w krótkim czasie.

  • Interfejs i backend mogą bezpiecznie komunikować się ze sobą, aby wymieniać dane logowania.

  • Przeglądarka obsługuje WebAuthn i tworzenie kluczy dostępu.

W sekcjach poniżej pokażemy Ci, jak sprawdzić większość z nich.

Gdy system spełni te warunki, zostanie przeprowadzony ten proces tworzenia klucza dostępu:

  1. System uruchamia proces tworzenia klucza dostępu, gdy użytkownik zainicjuje działanie (np. kliknie przycisk „Utwórz klucz dostępu” na stronie zarządzania kluczami dostępu lub po zakończeniu rejestracji).
  2. Interfejs wysyła do backendu żądanie niezbędnych danych logowania, w tym informacji o użytkowniku, wyzwania i identyfikatorów danych logowania, aby zapobiec duplikatom.
  3. Frontend wywołuje navigator.credentials.create(), aby poprosić dostawcę kluczy dostępu na urządzeniu o wygenerowanie klucza dostępu na podstawie informacji z backendu. Pamiętaj, że to wywołanie zwraca obietnicę.
  4. Urządzenie użytkownika uwierzytelnia go za pomocą metody biometrycznej, kodu PIN lub wzoru, aby utworzyć klucz dostępu.
  5. Dostawca klucza dostępu tworzy klucz dostępu i zwraca do frontend klucz publiczny, rozwiązując obietnicę.
  6. Frontend wysyła wygenerowane dane logowania klucza publicznego do backendu.
  7. Backend przechowuje klucz publiczny i inne ważne dane na potrzeby przyszłego uwierzytelniania.
  8. Backend powiadamia użytkownika (np. e-mailem) o potwierdzeniu utworzenia klucza dostępu i wykrywa potencjalny nieautoryzowany dostęp.

Ten proces zapewnia bezpieczną i bezproblemową rejestrację klucza dostępu dla użytkowników.

Zgodność

Większość przeglądarek obsługuje WebAuthn, ale są pewne drobne wyjątki. Szczegółowe informacje o zgodności z przeglądarkami i systemami operacyjnymi znajdziesz na stronie passkeys.dev.

Tworzenie nowego klucza dostępu

Aby utworzyć nowy klucz dostępu, interfejs powinien wykonać te czynności:

  1. Sprawdź zgodność.
  2. Pobieranie informacji z backendu
  3. Wywołaj interfejs WebAuth API, aby utworzyć klucz dostępu.
  4. Wyślij zwrócony klucz publiczny do backendu.
  5. Zapisz dane logowania.

W sekcjach poniżej dowiesz się, jak to zrobić.

Sprawdzanie zgodności

Przed wyświetleniem przycisku „Utwórz nowy klucz dostępu” interfejs powinien sprawdzić, czy:

  • Przeglądarka obsługuje WebAuthn z PublicKeyCredential.

Browser Support

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

Source

  • Przeglądarka obsługuje wykrywanie funkcji za pomocą elementu PublicKeyCredential.getClientCapabilities().

Browser Support

  • Chrome: 133.
  • Edge: 133.
  • Firefox: 135.
  • Safari: 17.4.

Source

  • Przeglądarka obsługuje interfejs warunkowy WebAuthnconditionalGet.

  • Urządzenie obsługuje mechanizm uwierzytelniający na platformie (może tworzyć klucze dostępu i uwierzytelniać się na urządzeniu) z passkeyPlatformAuthenticator.

Poniższy fragment kodu pokazuje, jak sprawdzić zgodność przed wyświetleniem opcji związanych z kluczem dostępu.

if (window.PublicKeyCredential && PublicKeyCredential.getClientCapabilities) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  if (capabilities.conditionalGet === true &&
      capabilities.passkeyPlatformAuthenticator === true) {
    // The browser supports passkeys and the conditional UI.
  }
}

W tym przykładzie przycisk Utwórz nowy klucz dostępu powinien być wyświetlany tylko wtedy, gdy wszystkie warunki są spełnione.

Pobieranie informacji z backendu

Gdy użytkownik kliknie przycisk, pobierz wymagane informacje z backendu, aby wywołać funkcję navigator.credentials.create().

Ten fragment kodu pokazuje obiekt JSON z informacjami wymaganymi 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: wygenerowane przez serwer wyzwanie w formacie ArrayBuffer dla tej rejestracji.
  • rp.id:Identyfikator RP (Relying Party ID), domena i witryna mogą określać swoją domenę lub sufiks, który można zarejestrować. Jeśli na przykład pochodzenie RP to https://login.example.com:1337, identyfikator RP może mieć postać login.example.com lub example.com. Jeśli identyfikator RP jest określony jako example.com, użytkownik może uwierzytelnić się w login.example.com lub w dowolnej subdomenie w example.com. Więcej informacji znajdziesz w artykule Zezwalanie na ponowne używanie kluczy dostępu w witrynach za pomocą żądań dotyczących powiązanych domen.
  • rp.name: nazwa RP (Relying Party). Zostało ono wycofane w WebAuthn L3, ale jest uwzględnione ze względu na zgodność.
  • user.id: Unikalny identyfikator użytkownika w ArrayBuffer, generowany 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. Prawdopodobnie masz już w swoim systemie identyfikator użytkownika, ale w razie potrzeby utwórz go specjalnie na potrzeby kluczy dostępu, aby nie zawierał żadnych informacji umożliwiających identyfikację.
  • user.name: Unikalny identyfikator konta, który użytkownik rozpozna, np. adres e-mail lub nazwa użytkownika. Będzie on wyświetlany w selektorze kont.
  • user.displayName: Wymagana, bardziej przyjazna dla użytkownika nazwa konta. Nie musi być unikalna i może być wybraną przez użytkownika nazwą. Jeśli Twoja witryna nie ma odpowiedniej wartości, którą można tu umieścić, przekaż pusty ciąg znaków. Może się ona wyświetlać w selektorze kont w zależności od przeglądarki.
  • pubKeyCredParams: określa algorytmy klucza publicznego obsługiwane przez RP (relying party). Zalecamy ustawienie wartości [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]. Określa to obsługę algorytmów ECDSA z P-256 i RSA PKCS#1, co zapewnia pełny zasięg.
  • excludeCredentials: Lista zarejestrowanych identyfikatorów danych logowania. Zapobiega dwukrotnej rejestracji tego samego urządzenia przez podanie listy zarejestrowanych identyfikatorów danych logowania. Element transports, jeśli jest podany, powinien zawierać wynik wywołania funkcji getTransports() podczas rejestracji każdego poświadczenia.
  • authenticatorSelection.authenticatorAttachment: ustaw wartość "platform" wraz z parametrem hint: ['client-device'], jeśli tworzenie klucza dostępu jest uaktualnieniem hasła, np. w ramach promocji po zalogowaniu się. "platform" oznacza, że RP chce uwierzytelniania platformy (uwierzytelniania wbudowanego w urządzenie platformy), które nie wyświetla na przykład prośby o włożenie klucza bezpieczeństwa USB. Użytkownik ma prostszą opcję utworzenia klucza dostępu.
  • authenticatorSelection.requireResidentKey: Ustaw wartość logiczną true. Dane logowania, które można wykryć (klucz rezydentny) przechowują informacje o użytkowniku w kluczu dostępu i umożliwiają użytkownikom wybór konta podczas uwierzytelniania.
  • authenticatorSelection.userVerification: określa, czy weryfikacja użytkownika za pomocą blokady ekranu urządzenia jest "required", "preferred" czy "discouraged". Domyślna wartość to "preferred", co oznacza, że uwierzytelnianie może pominąć weryfikację użytkownika. Ustaw tę wartość na "preferred" lub pomiń tę właściwość.

Zalecamy utworzenie obiektu na serwerze, zakodowanie ArrayBuffer za pomocą Base64URL i pobranie go z interfejsu. W ten sposób możesz zdekodować ładunek za pomocą PublicKeyCredential.parseCreationOptionsFromJSON() i przekazać go bezpośrednio do navigator.credentials.create().

Poniższy fragment kodu pokazuje, jak pobrać i zdekodować 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łaj interfejs WebAuthn API, aby utworzyć klucz dostępu

Wywołaj navigator.credentials.create(), aby utworzyć nowy klucz dostępu. Interfejs API zwraca obietnicę, czekając na interakcję użytkownika, która wyświetli okno modalne.

Browser Support

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

Source

// Invoke WebAuthn to create a passkey.
const credential = await navigator.credentials.create({
  publicKey: options
});

Wyślij zwrócone poświadczenie klucza publicznego do backendu.

Po zweryfikowaniu użytkownika za pomocą blokady ekranu urządzenia tworzony jest klucz dostępu, a obietnica jest spełniana przez zwrócenie do interfejsu obiektu PublicKeyCredential.

Obietnica może zostać odrzucona z różnych powodów. Możesz sobie z nimi poradzić, sprawdzając właściwość name obiektu Error:

  • InvalidStateError – na urządzeniu jest już klucz dostępu. Użytkownikowi nie będzie wyświetlane okno dialogowe z błędem. Witryna nie powinna traktować tego jako błędu. Użytkownik chciał zarejestrować urządzenie lokalne i zostało ono zarejestrowane.
  • NotAllowedError: użytkownik anulował operację.
  • AbortError: operacja została przerwana.
  • Inne wyjątki: wystąpił nieoczekiwany błąd. Przeglądarka wyświetla użytkownikowi okno dialogowe z błędem.

Obiekt danych uwierzytelniających klucza publicznego zawiera te właściwości:

  • id: zakodowany w formacie Base64URL identyfikator utworzonego klucza dostępu. Ten identyfikator pomaga przeglądarce określić, czy podczas uwierzytelniania na urządzeniu znajduje się pasujący klucz dostępu. Ta wartość musi być przechowywana w bazie danych na backendzie.
  • rawId: wersja identyfikatora danych logowania w formacie ArrayBuffer.
  • response.clientDataJSON: Dane klienta zakodowane w ArrayBuffer.
  • response.attestationObject: obiekt atestu zakodowany w ArrayBuffer. Zawiera on ważne informacje, takie jak identyfikator RP, flagi i klucz publiczny.
  • authenticatorAttachment: zwraca wartość "platform", gdy te dane logowania 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(), serializuj go za pomocą JSON.stringify(), a następnie 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 na backendzie danych logowania za pomocą klucza publicznego zalecamy użycie biblioteki po stronie serwera lub rozwiązania zamiast pisania własnego kodu do przetwarzania danych logowania za pomocą klucza publicznego.

Możesz następnie zapisać informacje pobrane z poświadczenia w bazie danych, aby użyć ich w przyszłości.

Poniżej znajdziesz listę zalecanych właściwości do zapisania:

  • Identyfikator danych logowania: Identyfikator danych logowania zwrócony z danymi logowania klucza publicznego.
  • Nazwa certyfikatu: nazwa certyfikatu. Nazwij go nazwą dostawcy klucza dostępu, który go utworzył. Możesz ją rozpoznać na podstawie identyfikatora AAGUID.
  • Identyfikator użytkownika: identyfikator użytkownika użyty do utworzenia klucza dostępu.
  • Klucz publiczny: Klucz publiczny zwrócony wraz z danymi logowania klucza publicznego. Jest to wymagane do weryfikacji potwierdzenia klucza dostępu.
  • Data i godzina utworzenia: zapisz datę i godzinę utworzenia klucza dostępu. Ułatwia to identyfikację klucza dostępu.
  • Data i godzina ostatniego użycia: rejestruje ostatnią datę i godzinę, kiedy użytkownik użył klucza dostępu do zalogowania się. Jest to przydatne do określania, którego klucza dostępu użytkownik użył (lub nie użył).
  • AAGUID: unikalny identyfikator dostawcy klucza dostępu.
  • Flaga kwalifikowania się do tworzenia kopii zapasowej: wartość „true”, jeśli urządzenie kwalifikuje się do synchronizacji kluczy dostępu. Te informacje pomagają użytkownikom rozpoznawać na stronie zarządzania kluczami dostępu klucze dostępu, które można zsynchronizować, oraz klucze dostępu powiązane z urządzeniem (których nie można zsynchronizować).

Bardziej szczegółowe instrukcje znajdziesz w artykule Rejestracja klucza dostępu po stronie serwera.

sygnalizować, czy rejestracja się nie powiodła;

Jeśli rejestracja klucza dostępu się nie powiedzie, może to wprowadzić użytkownika w błąd. Jeśli u dostawcy kluczy dostępu jest klucz dostępu dostępny dla użytkownika, 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 powiodą i trudno będzie rozwiązać ten problem. Poinformuj użytkownika, jeśli tak jest.

Aby temu zapobiec, możesz sygnalizować nieznany klucz dostępu dostawcy kluczy dostępu za pomocą interfejsu Signal API. Wywołując PublicKeyCredential.signalUnknownCredential() z identyfikatorem RP i identyfikatorem danych logowania, RP może poinformować dostawcę klucza dostępu, że określone dane logowania zostały usunięte lub nie istnieją. To dostawca klucza dostępu decyduje, jak postąpić w przypadku tego sygnału, ale jeśli jest on obsługiwany, powiązany 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 Utrzymywanie spójności kluczy dostępu z danymi logowania na serwerze za pomocą interfejsu Signal API.

Wysyłanie powiadomienia do użytkownika

Wysyłanie powiadomień (np. e-maili) po zarejestrowaniu klucza dostępu pomaga użytkownikom wykrywać nieautoryzowany dostęp do konta. Jeśli osoba atakująca utworzy klucz dostępu bez wiedzy użytkownika, klucz ten będzie dostępny do wykorzystania w przyszłości, nawet po zmianie hasła. Powiadomienie ostrzega użytkownika i pomaga zapobiec tej sytuacji.

Lista kontrolna

  • Zanim zezwolisz użytkownikowi na utworzenie klucza dostępu, zweryfikuj go (najlepiej za pomocą adresu e-mail lub bezpiecznej metody).
  • Uniemożliwia tworzenie zduplikowanych kluczy dostępu u tego samego dostawcy kluczy dostępu za pomocą funkcji excludeCredentials.
  • Zapisz identyfikator AAGUID, aby zidentyfikować dostawcę klucza dostępu i nazwać dane logowania użytkownika.
  • Sygnalizuj, czy próba zarejestrowania klucza dostępu nie powiodła się z powodu błędu PublicKeyCredential.signalUnknownCredential().
  • Po utworzeniu i zarejestrowaniu klucza dostępu na koncie użytkownika wyślij mu powiadomienie.

Zasoby

Następny krok: logowanie się za pomocą klucza dostępu dzięki autouzupełnianiu formularza.