spinny:~/writing $ less passkeys-webauthn-passwordless-authentication.md
12Hasła to jedna z tych rzeczy, które znormalizowaliśmy tylko dlatego, że żyjemy z nimi od lat. Użytkownicy o nich zapominają, wykorzystują je ponownie, zapisują tam, gdzie nie powinni. Zespoły muszą zarządzać resetowaniem, zasadami, skrótami, wyciekami, phishingiem i wsparciem.34passkey nie czyni uwierzytelnienia doskonałym, ale usuwa ogromny problem: serwer nie musi już zachowywać tajemnicy udostępnianej użytkownikowi.56## Co się naprawdę dzieje78passkey to referencja oparta na WebAuthn. Gdy użytkownik go utworzy, urządzenie generuje parę kluczy:910- klucz prywatny, który pozostaje na urządzeniu lub w menedżerze haseł;11- klucz publiczny, który serwer może zapisać.1213Podczas logowania serwer nie pyta "podaj mi hasło". Wyślij losowe wyzwanie. Urządzenie podpisuje go kluczem prywatnym. Serwer weryfikuje podpis kluczem publicznym.1415To właśnie jest fajne: jeśli baza danych zostanie skradziona, nie będzie w niej żadnych haseł, które można by złamać. A jeśli użytkownik trafi na fałszywą domenę, passkey nie będzie ważne dla tej domeny. To nie tylko wygoda, to konkretna ochrona przed phishingiem.1617## Przeglądarka działa jak pomost1819W przeglądarce dwa główne API to:2021- `navigator.credentials.create()`, aby utworzyć passkey;22- `navigator.credentials.get()`, aby użyć go podczas logowania.2324Ale najważniejsza logika leży na serwerze. Serwer musi wygenerować wyzwanie, zapisać je tymczasowo, zweryfikować odpowiedź, sprawdzić źródło i Relying Party ID, a następnie utworzyć sesję.2526Część klienta powinna być prawie nudna:2728```typescript29const options = await fetch('/api/passkeys/login/options').then((r) => r.json());3031const credential = await navigator.credentials.get({32 publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options),33});3435await fetch('/api/passkeys/login/verify', {36 method: 'POST',37 headers: { 'Content-Type': 'application/json' },38 body: JSON.stringify(credential?.toJSON()),39});40```4142Jeśli zauważysz, że ręcznie wdrażasz szyfrowanie WebAuthn, przestań. Użyj solidnej biblioteki po stronie serwera. Błędy tutaj nie są „słodkimi błędami”, są to luki w uwierzytelnianiu.4344## Co zapisać w bazie danych4546Nie ma potrzeby ratowania połowy świata. Zwykle wystarczy:4748- Identyfikator referencji;49- klucz publiczny;50- podłączony użytkownik;51- dowolny licznik weryfikacyjny lub metadane;52- transporty, jeśli są przydatne dla UX;53- nazwa wybrana przez użytkownika;54- data utworzenia i ostatniego użycia.5556Minimalna tabela może wyglądać następująco:5758```sql59create table passkey_credentials (60 id uuid primary key default gen_random_uuid(),61 user_id uuid not null references users(id),62 credential_id text not null unique,63 public_key text not null,64 name text,65 created_at timestamptz not null default now(),66 last_used_at timestamptz67);68```6970Następnie dodałbym logi audytu i powiadomienia: jeśli ktoś utworzy nowe passkey na moim koncie, chcę o tym wiedzieć.7172## UX jest ważniejszy niż demo7374Demo passkey jest zawsze piękne: klikasz, Face ID i zaczynasz. Rzeczywisty produkt jest bardziej skomplikowany.7576Ktoś zmienia telefon. Ktoś korzysta z zablokowanego komputera firmowego. Niektórzy ludzie nie rozumieją, dlaczego przeglądarka sugeruje passkey. Ktoś traci dostęp do swojego urządzenia.7778Dlatego nie zaczynałbym od „od dzisiaj koniec z hasłami dla wszystkich”. Zacząłbym tak:79801. passkey opcjonalne dla użytkowników wewnętrznych;812. sugestia utworzenia go po udanym logowaniu;823. strona konta do zmiany nazwy i usunięcia passkey;834. wyraźne wycofanie;845. stopniowe wdrażanie w głównym loginie.8586Tekst w interfejsie powinien być prosty. „Użyj ekranu blokady swojego urządzenia” jest lepsze niż „uwierzytelnij się za pomocą danych uwierzytelniających rezydenta FIDO2”.8788## Błędy, których chciałbym uniknąć8990Nie generuj wyzwań dla klienta. Wyzwanie tworzone jest na serwerze i należy je zweryfikować tylko raz.9192Nie ufaj tylko identyfikatorowi danych uwierzytelniających. Musisz zweryfikować podpis, wyzwanie, pochodzenie i Relying Party ID.9394Nie usuwaj kopii zapasowych, zanim nie uzyskasz dobrego przepływu odzyskiwania. Bez hasła nie musi oznaczać, że „jeśli zgubisz telefon, przepadniesz na zawsze”.9596Nie traktuj passkey jako przycisku czysto frontendowego. Ukrywanie przycisku nie zapewnia bezpieczeństwa: prawdziwa weryfikacja odbywa się po stronie serwera.9798## Passkey-pierwszy czy passkey-przyjazny?99100W przypadku nowego produktu możesz najpierw pomyśleć o passkey. W przypadku istniejącej aplikacji wolę przyjazną passkey: dodaj passkey jako zalecaną metodę, mierz sukcesy i problemy, a następnie spokojnie zmniejsz wagę hasła.101102Idealna migracja nie jest odczuwalna. Użytkownik odkrywa, że logowanie jest łatwiejsze, a nie, że firma zmieniła protokół uwierzytelniania.103104## Wniosek105106passkey są interesujące, ponieważ poprawiają jednocześnie bezpieczeństwo i UX, co jest rzadkością. To nie jest magiczna różdżka: odzyskiwanie, kompatybilność, wsparcie i wdrożenie muszą zostać dobrze zaprojektowane.107108Ale podstawowa zmiana jest silna. Przestań prosić użytkowników o wymyślanie i chronienie tajemnic. Pozwalasz urządzeniu podpisać dowód kryptograficzny powiązany z Twoją domeną. Mniej pamięci ludzkiej, mniej phishingu, mniej resetowania haseł. Powiedziałbym, że warto je traktować poważnie.109110## Źródła111112- [MDN: Passkeys](https://developer.mozilla.org/en-US/docs/Web/Security/Authentication/Passkeys)113- [MDN: Web Authentication API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API)114- [W3C: Web Authentication](https://www.w3.org/TR/webauthn-3/)115- [passkeys.dev](https://passkeys.dev/)116
:Passkey i WebAuthn: logowanie bez hasła, bez magiilines 1-116 (END) — press q to close