fix(webauthn)!: forward the access session at passkey enrollment - #155
Merged
Conversation
/webAuthn/register/start and /webAuthn/register/finish read the registration cookie and sent the ephemeral token upstream. The auth API mints one of those for an account that already exists, from an email address alone, so anyone who knew an address could enroll a credential against the account and sign in as its owner. Both routes now read the access cookie and forward the access token, in the core cookie table, the Express route and the Fastify proxy table. /webAuthn/login/start and /webAuthn/login/finish are unchanged and still take the pre-auth cookie, because authenticating is what they are for. No shipped flow loses a step. Registration proves an address with an email OTP, and verifying it issues a session, so the client holds an access cookie by the time it offers a passkey. finishRegisterHandler no longer issues session cookies. Enrolling a passkey is not a sign-in, and the caller now arrives holding a session, so minting a second one left the first live and unrevoked while counting against the API's concurrent session limit, which can evict the user's other devices. The route still answers 204. FinishRegisterOptions drops the four cookie options it no longer needs and FinishRegisterResult drops setCookies. Both adapters are updated. This is coordinated with the matching seamless-auth-api release, which refuses a pre-auth token on these two routes. The pre-auth routes' silent refresh still mints an access token into a cookie those routes cannot use. That is a separate defect and is left alone here.
…kstep An auth-mode change has no safe release order. An older adapter sends the token the new API refuses, and this release sends one an older API refuses, so enrollment answers 401 until both sides land. The changeset is where an adopter reads that, so it says it.
This was referenced Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Coordinated with fells-code/seamless-auth-api#287. Neither can merge alone.
Summary
/webAuthn/register/startand/webAuthn/register/finishread the registration cookie andsent the ephemeral token upstream. The auth API mints one of those for an account that
already exists, from an email address alone, so anyone who knew an address could enroll a
credential against the account and sign in as its owner. Both routes now read the access
cookie and forward the access token.
Changed in all three places the route table lives, so the adapters cannot drift:
packages/core/src/ensureCookies.tscookie requirementspackages/express/src/createServer.tsproxy identitypackages/fastify/src/routes/proxyRoutes.tsproxy table/webAuthn/login/startand/webAuthn/login/finishare unchanged and still take thepre-auth cookie.
No shipped flow loses a step. Registration proves an address with an email OTP, and
verifying it issues a session, so the client holds an access cookie by the time it offers a
passkey. An application that offered enrollment before verifying an address has to move that
step after it.
finishRegisterHandlerno longer issues session cookiesEnrolling a passkey is not a sign-in, and the caller now arrives holding a session, so
minting a second one left the first live and unrevoked while counting against the API's
concurrent session limit, which can evict the user's other devices. The auth API stopped
returning tokens here for the same reason. The route still answers
204.Public API change:
FinishRegisterOptionsdropsaudience,cookieDomain,accessCookieNameandrefreshCookieName, andFinishRegisterResultdropssetCookies.Only the two adapters in this workspace passed them and both are updated. Code calling
finishRegisterHandlerdirectly should pass{ authServerUrl }alone. Leaving requiredoptions that do nothing seemed worse than the break, since the next reader would reasonably
assume enrollment still issues a session.
Lockstep
There is no safe release order between this and the auth API. An older adapter sends the
token the new API refuses, and this sends one an older API refuses, so enrollment answers
401until both land. Merge and release with fells-code/seamless-auth-api#287 andfells-code/seamless-auth-react#147.
Verification
pnpm -r buildclean. Tests: core 246, express 156 (was 154), fastify 52 (was 48).New coverage in both adapters: enrollment on an access cookie succeeds and forwards
Bearer access-token; a registration cookie is refused401with no upstream call. Theparity table gets both cases plus a
401pinned by value, because a parity assertion alonewould pass just as happily if both adapters agreed on the wrong answer.
Reverting the Fastify identity and the core cookie table fails exactly those cases,
including the parity one.
Not in scope
The pre-auth routes' silent refresh still mints an access token into a cookie those routes
cannot use. That is what produced the
typ mismatchin the original report, enrollment nolonger hits it, and passkey login still does. Filed separately as #154.