Wachtwoorden zijn een van die dingen die we hebben genormaliseerd, alleen maar omdat we er al jaren mee leven. Gebruikers vergeten ze, hergebruiken ze, schrijven ze waar ze niet mogen. Teams moeten resets, beleid, hashes, lekken, phishing en ondersteuning beheren.
De passkey maken de authenticatie niet perfect, maar ze nemen een groot probleem weg: de server hoeft niet langer een geheim te bewaren dat met de gebruiker wordt gedeeld.
Wat er echt gebeurt
Een passkey is een referentie gebaseerd op WebAuthn. Wanneer de gebruiker het aanmaakt, genereert het apparaat een sleutelpaar:
- een privésleutel, die op het apparaat of in de wachtwoordbeheerder blijft staan;
- een publieke sleutel, die de server kan opslaan.
Bij het inloggen vraagt de server niet "vertel mij het wachtwoord". Stuur een willekeurige uitdaging. Het apparaat ondertekent het met de privésleutel. De server verifieert de handtekening met de publieke sleutel.
Dat is het leuke: als de database wordt gestolen, zijn er geen wachtwoorden meer om te kraken. En als een gebruiker op een nepdomein terechtkomt, is de passkey niet geldig voor dat domein. Het is niet alleen gemak, het is concrete bescherming tegen phishing.
De browser fungeert als een brug
In de browser zijn de twee belangrijkste API:
navigator.credentials.create()om een passkey te maken;navigator.credentials.get()om het te gebruiken tijdens het inloggen.
Maar de belangrijke logica zit op de server. De server moet de uitdaging genereren, deze tijdelijk opslaan, het antwoord verifiëren, de bron en Relying Party ID controleren en vervolgens de sessie creëren.
Het klantgedeelte zou bijna saai moeten zijn:
const options = await fetch('/api/passkeys/login/options').then((r) => r.json()); const credential = await navigator.credentials.get({ publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options), }); await fetch('/api/passkeys/login/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credential?.toJSON()), });
Als u merkt dat u WebAuthn-codering handmatig implementeert, stop dan. Gebruik een robuuste server-side bibliotheek. De fouten hier zijn geen "schattige bugs", het zijn authenticatiegaten.
Wat u in de database moet opslaan
Het is niet nodig om de halve wereld te redden. Meestal genoeg:
- ID van de identificatie;
- publieke sleutel;
- aangesloten gebruiker;
- eventuele verificatieteller of metadata;
- transporten, indien nuttig voor de UX;
- naam gekozen door de gebruiker;
- aanmaakdatum en laatste gebruik.
Een minimale tabel zou er als volgt uit kunnen zien:
create table passkey_credentials ( id uuid primary key default gen_random_uuid(), user_id uuid not null references users(id), credential_id text not null unique, public_key text not null, name text, created_at timestamptz not null default now(), last_used_at timestamptz );
Vervolgens zou ik auditlogboeken en meldingen toevoegen: als iemand een nieuwe passkey op mijn account aanmaakt, wil ik daarvan op de hoogte zijn.
UX is belangrijker dan demo
De demo van een passkey is altijd mooi: je klikt, Face ID, je doet mee. Het eigenlijke product is ingewikkelder.
Iemand verandert van telefoon. Iemand gebruikt een vergrendelde bedrijfscomputer. Sommige mensen begrijpen niet waarom de browser een passkey voorstelt. Iemand verliest de toegang tot zijn apparaat.
Daarom zou ik niet beginnen met "vanaf vandaag geen wachtwoorden meer voor iedereen". Ik zou zo beginnen:
- passkey optioneel voor interne gebruikers;
- suggestie om er een aan te maken na een succesvolle login;
- accountpagina om passkey te hernoemen en te verwijderen;
- duidelijke terugval;
- geleidelijke uitrol in de hoofdlogin.
De tekst in de interface moet eenvoudig zijn. 'Gebruik het vergrendelingsscherm van uw apparaat' is beter dan 'authenticeren met een inwoner FIDO2-referentie'.
Fouten die ik zou vermijden
Genereer geen uitdagingen bij de cliënt. De uitdaging wordt op de server aangemaakt en hoeft slechts één keer te worden geverifieerd.
Vertrouw niet alleen op de inlog-ID. U moet de handtekening, uitdaging, herkomst en Relying Party ID verifiëren.
Verwijder geen fallbacks voordat u een goede herstelstroom heeft. Wachtwoordloos hoeft niet te worden "als je je telefoon kwijtraakt, ben je voor altijd weg".
Beschouw de passkey niet als een puur frontend-knop. Het verbergen van een knop is geen beveiliging: de echte verificatie vindt plaats aan de serverzijde.
Passkey-eerst of passkey-vriendelijk?
Bij een nieuw product kun je passkey eerst denken. Voor een bestaande app geef ik de voorkeur aan passkey-vriendelijk: voeg passkey toe als aanbevolen methode, meet succes en problemen en verlaag vervolgens rustig het wachtwoordgewicht.
De ideale migratie wordt niet gevoeld. De gebruiker ontdekt dat inloggen makkelijker gaat, niet dat het bedrijf zijn authenticatieprotocol heeft gewijzigd.
Conclusie
De passkey zijn interessant omdat ze tegelijkertijd de beveiliging en UX verbeteren, wat zeldzaam is. Ze zijn geen toverstaf: herstel, compatibiliteit, ondersteuning en uitrol moeten nog goed worden ontworpen.
Maar de fundamentele verandering is sterk. Vraag gebruikers niet langer om geheimen te verzinnen en te beschermen. U laat het apparaat een cryptografisch bewijs ondertekenen dat aan uw domein is gekoppeld. Minder menselijk geheugen, minder phishing, minder wachtwoordresets. Ik zou zeggen dat ze de moeite waard zijn om serieus te nemen.