feat(passkey): let a caller ask for a security key at enrollment - #137
Merged
Conversation
Closes #136. GET /webAuthn/register/start accepts an attachment that narrows the browser picker to one kind of authenticator, and nothing in this SDK could ask for it: buildRegisterStartPath set only the PRF flags, so reaching the parameter meant hand building the URL and reimplementing the PRF, metadata and error handling registerPasskey already does. registerPasskey now takes an optional attachment, and the bundled enrolment view offers a "Use a security key instead" control that takes it. That is the path an organisation handing someone an issued key needs, rather than leaving them to recognise it in a browser dialog. Omitting the option sends no query parameter, so the deployment's authenticator_policy.attachment stays in charge and the default path is unchanged. PasskeyAttachment is derived from AuthenticatorAttachmentPolicy with Exclude rather than restating the two strings: `any` is a standing deployment default, not something a single request can ask for, so the request type is that policy minus that member. The API publishes no request-side union of its own. PasskeyPolicyErrorCode gains attachment_not_allowed. It was left out in #135 because it was a 400 from register/start rather than a 403 from finish, and because nothing in the SDK could provoke it. Sending the parameter is what makes it reachable, and it is a refusal by authenticator_policy like the others, so it belongs with them. prf_output_not_allowed stays out: it reports a client that failed to strip PRF output, not a deployment refusing an authenticator. The enrolment view now explains a refusal instead of showing the generic failure, so a user told to reach for a security key can act on it. Verified with npm run typecheck, npm test (315 passing), npm run lint, npm run format:check, and npm run build.
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.
Closes #136. Client half of fells-code/seamless-auth-api#183.
Summary
GET /webAuthn/register/startaccepts anattachmentthat narrows the browserpicker to one kind of authenticator, and nothing in this SDK could ask for it.
buildRegisterStartPathset only the PRF flags, so reaching the parameter meanthand building the URL and reimplementing the PRF, metadata and error handling
registerPasskeyalready does.registerPasskey()now takes an optionalattachment, and the bundled enrolmentview offers a "Use a security key instead" control that takes it. That is the
path an organisation handing someone an issued key needs, rather than leaving
them to recognise it in a browser dialog.
Omitting the option sends no query parameter, so the deployment's
authenticator_policy.attachmentstays in charge and the default enrolment pathis unchanged. There is a test pinning that specifically.
PasskeyAttachmentDerived as
Exclude<AuthenticatorAttachmentPolicy, 'any'>rather than restatingthe two strings.
anyis a standing deployment default, not something a singlerequest can ask for, so the request type is that policy minus that member. The
API's own request-side union lives in the API repository and is not published in
@seamless-auth/types, so deriving it is what keeps this from drifting the wayPasskeyPolicyErrorCodedid before #132.attachment_not_allowedis now in scopeThis reverses part of #135, for a reason that only became true here.
That PR excluded
attachment_not_allowedon two grounds: it is a400fromregister/start rather than a
403from register/finish, and nothing in the SDKcould provoke it, since the API only sends it when the caller names an
attachment. Sending the parameter is exactly what makes it reachable, and it is
a refusal by
authenticator_policylike the others, so it belongs with them.getPasskeyPolicyErrorCodeis now documented as covering both stages.prf_output_not_allowedstays out on the surviving half of that argument: itreports a client that failed to strip PRF output, not a deployment refusing an
authenticator, and folding it in would point an integrator at their config for
what is a bug in the caller. Still pinned by a test.
Worth noting this is free to change:
getPasskeyPolicyErrorCodehas not shippedyet. The published version is 0.9.0 and its changeset is still pending, so no
consumer has the narrower union.
Enrolment view
The view previously swallowed every failure into "Error registering passkey."
A policy refusal names something the user can act on, so it now renders a
specific message and keeps the generic one for everything else. This is what
#132 asked for in its acceptance criteria and could not fully deliver from the
client layer alone.
Type of Change
Release Impact
Two changesets.
tidy-moons-repeatis new and covers the attachment option.olive-pears-shoutis the pending, unreleased one forgetPasskeyPolicyErrorCode,edited rather than supplemented: the helper ships in the same release, so a
changelog describing a three-code union followed by an entry widening it would
describe a history adopters never saw.
Checklist
npm run typecheck,npm test(315 passing, up from 305),npm run lint,npm run format:checkandnpm run buildall pass.The one behavioural change to an existing path is that the enrolment view now
calls
registerPasskey({ metadata, attachment })instead ofregisterPasskey(metadata). Both forms are accepted bynormalizeRegisterInput,and the existing test was updated to the new shape.