feat(passkey): let a signed-in user add a passkey - #147
Merged
Conversation
registerPasskey lived only on the client, so the bundled UI promised something the package did not implement: "You can continue without one and add a passkey later from a device that does". credentials could be listed and deleted but never added. It is now on useAuth() and on the framework-agnostic session actions, and the context version refreshes the session afterwards so a settings screen renders the new passkey without a reload. Enrollment requires a signed-in session as of the matching seamless-auth-api and adapter releases. The bundled flow already satisfies it: verifying the email OTP issues a session before the passkey screen is reached, so no view moves. An application that called registerPasskey() before verifying an address has to move that call after it. A 401 at enrollment now says the session expired rather than "Error registering passkey". It is the session rather than anything about the authenticator, and the generic wording invited a retry with the same expired one. isUnauthenticated(error) is exported for callers rendering their own screens. Also corrects a README example that still passed a token to registerPasskey, a field removed when the wire contract moved to @seamless-auth/types, and documents that enrollment comes after the step that establishes a session.
The auth API and the server adapter have no safe release order between them, so enrollment answers 401 until both land. An adopter upgrading one package at a time needs to read that here.
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.
Summary
Coordinated with fells-code/seamless-auth-api#287 and
fells-code/seamless-auth-server#155. Neither can merge alone.
registerPasskeylived only on the client, so the bundled UI promised something the packagedid not implement: "You can continue without one and add a passkey later from a device that
does" (PassKeyRegistration.tsx:110).
credentialscould be listed and deleted but never added. It is now onuseAuth()and onthe framework-agnostic session actions, and the context version refreshes the session
afterwards so a settings screen renders the new passkey without a reload.
Enrollment requires a signed-in session as of the matching API and adapter releases. The
bundled flow already satisfies it, because verifying the email OTP issues a session before
the passkey screen is reached, so no view moves and no request path changes. An
application that called
registerPasskey()before verifying an address has to move thatcall after it.
A
401at enrollment now says the session expired rather than "Error registering passkey."It is the session rather than anything about the authenticator, and the generic wording
invited a retry with the same expired one.
isUnauthenticated(error)is exported forcallers rendering their own screens.
Also corrects a README example that still passed a
tokentoregisterPasskey, a fieldremoved when the wire contract moved to
@seamless-auth/types, and documents thatenrollment comes after the step that establishes a session.
Type of Change
Release Impact
minor. Releases in lockstep with the API and the adapter: there is no safe order betweenthose two, and enrollment answers
401until both land.Checklist
On breaking changes: nothing in this package's own API breaks, but enrollment now
requires a session because of the coordinated API change, so the box stays unchecked
honestly.
On security: this is the client half of closing an account-takeover path.
/loginand/registration/registerboth mint a pre-auth token for an existing account from an emailaddress alone, and enrollment used to accept it.
Verification
npm run typecheck,npm run lint,npm run format:checkandnpm run buildall clean.Tests: 32 suites, 327 passed (was 324).
New coverage: the session store adds an enrolled passkey to
credentialsand leaves statealone when enrollment fails; the view renders the re-authenticate message on a
401and notthe generic one. Reverting either behaviour fails four tests.
Testing locally
Link this and the adapter into a test app rather than pulling published versions, since all
three move together. The two paths worth exercising are enrolling during signup, which is
what #278 broke, and adding a second passkey from a signed-in screen, which was not possible
before at all.