Le password sono una di quelle cose che abbiamo normalizzato solo perché ci conviviamo da anni. Gli utenti le dimenticano, le riusano, le scrivono dove non dovrebbero. I team devono gestire reset, policy, hash, leak, phishing e supporto.
Le passkey non rendono l'autenticazione perfetta, ma tolgono di mezzo un problema enorme: il server non deve più custodire un segreto condiviso con l'utente.
Cosa succede davvero
Una passkey è una credenziale basata su WebAuthn. Quando l'utente la crea, il dispositivo genera una coppia di chiavi:
- una chiave privata, che resta sul dispositivo o nel password manager;
- una chiave pubblica, che il server può salvare.
Al login il server non chiede "dimmi la password". Manda una challenge casuale. Il dispositivo la firma con la chiave privata. Il server verifica la firma con la chiave pubblica.
Questa è la parte bella: se il database viene rubato, dentro non ci sono password da crackare. E se un utente finisce su un dominio falso, la passkey non è valida per quel dominio. Non è solo comodità, è una protezione concreta contro il phishing.
Il browser fa da ponte
Nel browser le due API principali sono:
navigator.credentials.create()per creare una passkey;navigator.credentials.get()per usarla durante il login.
Ma la logica importante sta sul server. Il server deve generare la challenge, salvarla temporaneamente, verificare la risposta, controllare origine e Relying Party ID, poi creare la sessione.
La parte client dovrebbe essere quasi noiosa:
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()), });
Se ti ritrovi a implementare a mano la crittografia WebAuthn, fermati. Usa una libreria solida lato server. Gli errori qui non sono "bug carini", sono buchi di autenticazione.
Cosa salvare nel database
Non serve salvare mezzo mondo. Di solito bastano:
- ID della credenziale;
- chiave pubblica;
- utente collegato;
- eventuale counter o metadati di verifica;
- transports, se utili alla UX;
- nome scelto dall'utente;
- data di creazione e ultimo utilizzo.
Una tabella minima può assomigliare a questa:
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 );
Poi aggiungerei audit log e notifiche: se qualcuno crea una nuova passkey sul mio account, voglio saperlo.
La UX conta più della demo
La demo di una passkey è sempre bellissima: clicchi, Face ID, sei dentro. Il prodotto reale è più complicato.
Qualcuno cambia telefono. Qualcuno usa un computer aziendale bloccato. Qualcuno non capisce perché il browser propone una passkey. Qualcuno perde accesso al proprio dispositivo.
Per questo non partirei con "da oggi niente più password per tutti". Partirei così:
- passkey opzionali per utenti interni;
- suggerimento di crearne una dopo un login riuscito;
- pagina account per rinominare e rimuovere passkey;
- fallback chiaro;
- rollout graduale nel login principale.
Il testo nell'interfaccia deve essere semplice. "Usa il blocco schermo del tuo dispositivo" è meglio di "autenticati con una credenziale FIDO2 residente".
Errori che eviterei
Non generare challenge sul client. La challenge nasce sul server e deve essere verificata una sola volta.
Non fidarti solo del credential ID. Devi verificare firma, challenge, origine e Relying Party ID.
Non cancellare i fallback prima di avere un buon recovery flow. Passwordless non deve diventare "se perdi il telefono sei fuori per sempre".
Non trattare la passkey come un bottone puramente frontend. Nascondere un pulsante non è sicurezza: la verifica vera è server-side.
Passkey-first o passkey-friendly?
Per un prodotto nuovo puoi pensare passkey-first. Per un'app esistente io preferisco passkey-friendly: aggiungi passkey come metodo consigliato, misuri successo e problemi, poi riduci il peso delle password con calma.
La migrazione ideale non si sente. L'utente scopre che accedere è più semplice, non che l'azienda ha cambiato protocollo di autenticazione.
Conclusione
Le passkey sono interessanti perché migliorano sicurezza e UX nello stesso momento, cosa rara. Non sono una bacchetta magica: recovery, compatibilità, supporto e rollout restano da progettare bene.
Però il cambio di base è forte. Smetti di chiedere agli utenti di inventare e proteggere segreti. Lasci che il dispositivo firmi una prova crittografica legata al tuo dominio. Meno memoria umana, meno phishing, meno reset password. Direi che vale la pena prenderle sul serio.