Coulisses

WebAuthn ou rien — le soir où j'ai compris que tout le reste était mort

Un soir, mon agent me lance : « Tu as essayé WebAuthn ? » 3 heures plus tard, j'avais supprimé tous mes mots de passe. Pourquoi login/password, OAuth et TOTP sont morts — et pourquoi on ne revient pas en arrière.

Plaque de laiton gravée WEBAUTHN au premier plan sur un établi ; derrière, un atelier en bois explose, projetant débris et étincelles dans une lueur orangée.
WebAuthn gravé dans le laiton — pendant que l'atelier des anciennes méthodes vole en éclats.

Un soir de mai 2026, l'agent que j'utilise tous les jours m'a posé une question. « Tu as essayé WebAuthn ? » Je suis allé chercher de quoi il parlait. 3 heures plus tard, j'avais supprimé tous les mots de passe de home-ai, et je n'avais toujours pas refermé l'onglet de la spec W3C.

Ce qui suit est l'histoire d'une découverte qui a déplacé une croyance. Pas un tutoriel sur WebAuthn — il en existe d'excellents, je donnerai les liens. Une explication de pourquoi, une fois qu'on a vu WebAuthn fonctionner, on ne peut plus considérer le reste comme acceptable.

Mon parcours en quatre étapes

Je développe depuis près de 30 ans, et pourtant j'ai découvert tard ce que les standards proposaient déjà. Pas par paresse — par ignorance. Personne ne me l'avait mis sous le nez.

Étape 1 — Login/password. Quand j'ai commencé à coder haik (l'ancêtre de home-ai, raconté dans HAIK — le faux-départ), j'ai fait à la papa : une table users, un champ password_hash, bcrypt, formulaire de login. La sécurité dépendait de moi. Du sel que j'avais choisi. Du fait que je n'oublie pas de mettre https. Du fait qu'un dump de la base ne fuite jamais.

Étape 2 — OAuth. J'avais déjà fait OAuth plusieurs fois, dans plusieurs langages, pour des intégrations. C'est une délégation d'autorisation : « GitHub, dis à mon app que cet utilisateur est bien celui qu'il prétend être ». Pratique pour intégrer une API tierce. Mais OAuth seul ne fait pas une authentification utilisateur propre — c'est OpenID Connect (OIDC) qui ajoute cette couche par-dessus.

Étape 3 — SSO + 2FA. Quand j'ai construit home-ai avec un LLM moderne, le LLM m'a proposé de ne pas stocker de mot de passe du tout. « Pourquoi tu n'utilises pas Authentik et tu laisses ton compte Google authentifier ? » J'ai répondu « je peux faire ça ? » — il a répondu « oui, en deux fichiers de config ». Au démarrage, home-ai n'avait littéralement pas de table users avec password. Seulement un identifiant Google + un TOTP via Google Authenticator. C'était déjà un saut.

Étape 4 — WebAuthn. Un soir donc. Et là, c'est différent. Ce n'est pas une amélioration des trois précédentes. C'est leur disparition.

Pourquoi les trois premières étapes sont mortes

Je veux être précis sur ce qui ne va plus, parce que chacune de ces méthodes paraît "OK" prise isolément. Ce qui les tue, c'est leur modèle de menace.

Login/password — le secret partagé est cassé par construction

Le serveur stocke un hash. L'utilisateur saisit un mot de passe. Le serveur compare. C'est simple, et c'est précisément ce qui le rend irréparable.

Tu peux mettre tout le bcrypt du monde — OWASP Password Storage Cheat Sheet, Argon2id, paramètres au max — ça n'enlève pas ce fait : le secret existe en clair quelque part, au moins le temps qu'il transite. L'utilisateur le réutilise sur 15 autres sites. Un de ces sites se fait pirater. Ton site tombe avec.

Le phishing fait le reste. Un email crédible, une fausse page de login, et l'utilisateur tape son mot de passe sur le serveur de l'attaquant. Le protocole ne sait pas distinguer.

OAuth/SSO — tu changes de problème, pas de propriétaire

Je ne stocke plus de mot de passe ? Bien. Google le stocke pour moi. Ce qui veut dire :

  • Le jour où Google ferme mon compte (ça arrive, sans préavis, sans appel), je perds l'accès à tout ce qui en dépend.
  • Le jour où un attaquant compromet mon compte Google (phishing, SIM swap), il a accès à tout ce qui en dépend.
  • Le jour où Google change ses CGU, je dois suivre.

OAuth2 (RFC 6749) et OIDC (spec OpenID) sont des protocoles solides. Le problème n'est pas le protocole — c'est le fait que je transfère mon risque chez un tiers qui n'a aucun engagement contractuel envers moi (compte gratuit).

2FA TOTP — un pansement honnête sur une plaie ouverte

TOTP (RFC 6238) — le code à 6 chiffres qui tourne toutes les 30 secondes — c'est élégant en théorie. Une fonction HMAC, un compteur basé sur le temps, un secret partagé entre l'app et le serveur.

Sauf que :

  • Le secret partagé est partagé. Quand le serveur leak, l'attaquant peut générer tes TOTP indéfiniment.
  • Le code est phishable. Une fausse page de login te demande tes 6 chiffres, l'attaquant les rejoue dans la trentaine de secondes. Vu mille fois.
  • L'UX est laborieuse. 6 chiffres à recopier à chaque fois.

TOTP n'est pas mauvais. Il est moins mauvais que rien. C'est un pansement.

WebAuthn — la rupture de modèle

Le coup de tonnerre, c'est ça : avec WebAuthn, il n'y a plus de secret partagé.

L'utilisateur a une clé privée — qui ne quitte jamais son appareil. Le serveur a la clé publique correspondante. Pour s'authentifier, le serveur envoie un défi aléatoire, l'appareil le signe avec la clé privée, le serveur vérifie la signature avec la clé publique.

Lis cette phrase deux fois. Il n'y a rien à voler côté serveur. Si la base leak, l'attaquant récupère des clés publiques — il ne peut rien en faire. Si l'utilisateur tombe dans une fausse page de phishing, la cérémonie WebAuthn vérifie l'origine du domaine (example.comexamp1e.com) et refuse de signer. L'utilisateur ne peut pas se faire phisher, même s'il essaie.

C'est ça qui m'a explosé le cerveau ce soir-là. Pas la cryptographie — la cryptographie asymétrique, je la connais. Ce qui m'a explosé le cerveau, c'est que cette cryptographie soit enfin intégrée au navigateur, standard W3C, disponible partout, et que personne autour de moi ne m'en ait jamais parlé.

Les deux cérémonies

WebAuthn a deux moments, et ils ressemblent étrangement aux deux moments de TLS.

Registration (inscription). L'utilisateur crée une nouvelle paire de clés sur son appareil. La clé privée reste là, protégée par le secure element (Touch ID, Windows Hello, Secure Enclave, TPM). La clé publique est envoyée au serveur, qui la stocke à côté de l'identifiant utilisateur. Une fois. Pour cet utilisateur, sur ce service.

Authentication (connexion). Le serveur envoie un défi (un nombre aléatoire). L'appareil signe le défi avec la clé privée — après avoir vérifié biométriquement que c'est bien le propriétaire qui le demande (empreinte, visage, PIN). La signature est envoyée au serveur, qui la vérifie avec la clé publique stockée. C'est tout.

Plateforme vs cross-platform

WebAuthn supporte deux types de keystores, et c'est important pour comprendre les passkeys.

Authenticator plateforme — la clé vit dans l'appareil (Touch ID, Windows Hello). Ergonomique mais lié à l'appareil. Si je casse mon laptop, j'ai perdu cette clé.

Authenticator cross-platform — la clé vit dans un device externe (YubiKey, clé Solo, smartphone via Bluetooth). Portable, mais il faut la trimballer.

Les passkeys, c'est WebAuthn + une couche de synchronisation. iCloud Keychain, Google Password Manager, 1Password, Bitwarden — ils synchronisent ta clé privée chiffrée entre tes appareils. Tu retrouves ton compte sur ton nouveau téléphone sans passer par "mot de passe oublié". Le débat philosophique clé non-extractible vs clé synchronisable est un vrai débat de cryptographes, mais pour 99% des cas, les passkeys sont la bonne réponse.

Les liens officiels — pour creuser

Je ne vais pas reproduire la spec dans cet article. Voici ce que je consulte quand j'implémente :

  • W3C — Web Authentication API — la spec de référence. Le Level 2 est la Recommandation stable (2021) ; le Level 3 (Candidate Recommendation, 2026) finalise les passkeys multi-appareils. Longue mais bien écrite.
  • FIDO Alliance — l'alliance industrielle derrière le protocole. Onglet "Resources" pour les guides d'implémentation.
  • passkeys.dev — le site de référence pour les passkeys, maintenu par la FIDO Alliance et le groupe WebAuthn du W3C. Avec des démos cliquables.
  • MDN — Web Authentication API — l'API navigateur côté JavaScript, avec exemples.
  • webauthn.io — un playground pour tester une cérémonie complète sans coder.

Pour la rétrospective, les standards que WebAuthn remplace :

L'objection honnête : le support n'est pas total

Soyons honnêtes : tous les utilisateurs n'ont pas un appareil compatible. Mais en 2026, on est très loin de l'époque pionnière. Tous les navigateurs récents supportent WebAuthn. Tous les smartphones de moins de 5 ans. Tous les laptops avec Touch ID, Windows Hello ou TPM 2.0. Pour ce qui reste, on peut garder TOTP comme filet — mais en présentant WebAuthn comme défaut.

L'autre objection — « mais si je casse mon téléphone ? » — est réglée par les passkeys synchronisées. Et même sans synchro, n'importe quel service sérieux propose plusieurs clés enregistrées (laptop + téléphone + YubiKey de backup dans un tiroir). C'est l'équivalent moderne du « plusieurs jeux de clés » qu'on a toujours fait avec les serrures physiques.

La leçon

Pendant toutes ces années, j'ai implémenté de l'authentification en pensant que c'était un problème résolu, juste verbeux. Ajouter une table users, hasher un mot de passe, c'était mécanique. Pénible mais maîtrisé.

WebAuthn m'a fait comprendre que c'était un problème mal posé. La question n'était pas « comment je stocke un mot de passe en sécurité ? ». La question était « pourquoi je stocke un secret du tout ? ».

Le LLM qui m'a posé la question ce soir-là ne savait probablement pas qu'il était en train de changer ma manière de concevoir l'identité. Il faisait son travail — me proposer un standard moderne en réponse à un besoin que j'exprimais mal. Mais c'est exactement à ça que ces outils servent : non pas à coder plus vite ce que tu sais déjà faire, mais à te dire « il existe une meilleure façon que tu ignores ».

Si tu lis ce blog et que tu n'as jamais touché à WebAuthn, ouvre webauthn.io maintenant. Clique sur "Register". Mets ton empreinte. Reviens me dire ce que tu as ressenti.

Pour ma part, le soir où j'ai compris, je n'ai pas fini d'implémenter. J'ai commencé.

— Damien


Voir aussi