spinny:~/writing $ vim passkeys-webauthn-passwordless-authentication.md
1~2Lösenord är en av de saker som vi har normaliserat bara för att vi har levt med dem i flera år. Användare glömmer dem, återanvänder dem, skriver dem där de inte borde. Team måste hantera återställningar, policyer, hash, läckor, nätfiske och support.3~4passkey gör inte autentisering perfekt, men de tar bort ett stort problem: servern behöver inte längre hålla en hemlighet som delas med användaren.5~6## Vad händer egentligen7~8En passkey är en legitimation baserad på WebAuthn. När användaren skapar den genererar enheten ett nyckelpar:9~10- en privat nyckel, som finns kvar på enheten eller i lösenordshanteraren;11- en publik nyckel som servern kan spara.12~13När du loggar in frågar servern inte "berätta lösenordet". Skicka en slumpmässig utmaning. Enheten signerar den med den privata nyckeln. Servern verifierar signaturen med den publika nyckeln.14~15Det är det fina: om databasen blir stulen finns det inga lösenord inuti att knäcka. Och om en användare hamnar på en falsk domän är passkey inte giltig för den domänen. Det är inte bara bekvämlighet, det är ett konkret skydd mot nätfiske.16~17## Webbläsaren fungerar som en brygga18~19I webbläsaren är de två huvudsakliga API:20~21- `navigator.credentials.create()` för att skapa en passkey;22- `navigator.credentials.get()` för att använda den under inloggning.23~24Men den viktiga logiken ligger på servern. Servern måste generera utmaningen, spara den tillfälligt, verifiera svaret, kontrollera källan och Relying Party ID, sedan skapa sessionen.25~26Klientdelen borde vara nästan tråkig:27~28```typescript29const options = await fetch('/api/passkeys/login/options').then((r) => r.json());30~31const credential = await navigator.credentials.get({32 publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options),33});34~35await fetch('/api/passkeys/login/verify', {36 method: 'POST',37 headers: { 'Content-Type': 'application/json' },38 body: JSON.stringify(credential?.toJSON()),39});40```41~42Om du kommer på att du implementerar WebAuthn-kryptering för hand, sluta. Använd ett robust bibliotek på serversidan. Felen här är inte "söta buggar", de är autentiseringshål.43~44## Vad ska sparas i databasen45~46Det finns ingen anledning att rädda halva världen. Vanligtvis nog:47~48- ID för legitimationen;49- offentlig nyckel;50- ansluten användare;51- alla verifieringsräknare eller metadata;52- transporter, om användbara för UX;53- namn valt av användaren;54- Skapandedatum och senaste användning.55~56En minimal tabell kan se ut så här:57~58```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```69~70Sedan skulle jag lägga till granskningsloggar och aviseringar: om någon skapar en ny passkey på mitt konto vill jag veta om det.71~72## UX betyder mer än demo73~74Demon av en passkey är alltid vacker: du klickar, Face ID, du är med. Den faktiska produkten är mer komplicerad.75~76Någon byter telefon. Någon använder en låst företagsdator. Vissa människor förstår inte varför webbläsaren föreslår en passkey. Någon förlorar åtkomst till sin enhet.77~78Det är därför jag inte skulle börja med "från och med idag inga fler lösenord för alla". Jag skulle börja så här:79~801. passkey valfritt för interna användare;812. förslag att skapa en efter en lyckad inloggning;823. kontosida för att byta namn och ta bort passkey;834. klara fallback;845. gradvis utbyggnad i huvudinloggningen.85~86Texten i gränssnittet ska vara enkel. "Använd din enhets låsskärm" är bättre än "autentisera med en invånare FIDO2 inloggningsuppgifter."87~88## Misstag jag skulle undvika89~90Generera inte utmaningar på klienten. Utmaningen skapas på servern och måste endast verifieras en gång.91~92Lita inte bara på legitimations-ID. Du måste verifiera signatur, utmaning, ursprung och Relying Party ID.93~94Ta inte bort fallbacks innan du har ett bra återställningsflöde. Lösenordslös behöver inte bli "om du tappar din telefon är du ute för alltid".95~96Behandla inte passkey som en ren frontend-knapp. Att dölja en knapp är inte säkerhet: den verkliga verifieringen är serversidan.97~98## Passkey-först eller passkey-vänlig?99~100För en ny produkt kan du tänka passkey-först. För en befintlig app föredrar jag passkey-vänlig: lägg till passkey som en rekommenderad metod, mät framgång och problem och minska sedan lugnt lösenordsvikten.101~102Den ideala migrationen känns inte. Användaren upptäcker att det är lättare att logga in, inte att företaget har ändrat sitt autentiseringsprotokoll.103~104## Slutsats105~106passkey är intressanta eftersom de förbättrar säkerheten och UX samtidigt, vilket är sällsynt. De är inte ett trollspö: återhämtning, kompatibilitet, stöd och utrullning återstår att utformas väl.107~108Men den grundläggande förändringen är stark. Sluta be användarna att uppfinna och skydda hemligheter. Du låter enheten signera ett kryptografiskt bevis kopplat till din domän. Mindre mänskligt minne, mindre nätfiske, färre lösenordsåterställningar. Jag skulle säga att de är värda att ta på allvar.109~110## Källor111~112- [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~
NORMAL · passkeys-webauthn-passwordless-authentication.md [readonly]116 lines · :q to close