You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(webauthn): give challenges their own store, with expiry and one-time use
Challenges lived in a single users.challenge column shared by registration,
login and step-up, so the three flows clobbered each other and a second tab
invalidated the first. The column also had no lifetime: the timeout in the
credential options is only a hint to the browser, and nothing server side ever
enforced it, so a challenge stayed valid until some later flow happened to
overwrite it.
Challenges now live in webauthn_challenges, keyed by user and flow, with a
server enforced five minute life. That is comfortably longer than the sixty
second client hint, so a user hunting for a security key is not cut off.
They are also spent when verification reads them, before anything else can fail,
so no outcome leaves a redeemable challenge behind. That closes the replay
finding tracked privately alongside this issue, whose fix belonged in the same
change.
The per-flow context that used to sit in users.challengeContext travels with the
challenge it was issued for.
users.challenge and users.challengeContext are no longer read or written. They
are left in place so this release can be rolled back, and should be dropped once
it has run in production.
Migration verified up and down against Postgres 17.
Closes#164
0 commit comments