diff --git a/AGENTS.md b/AGENTS.md index b84c833..4aa469f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -256,10 +256,19 @@ turns the code into `error.message`, so callers must branch with Keychain or Google Password Manager passkey is backup eligible, so this is the default path, not an edge case. -`@seamless-auth/types` publishes no union for those codes as of 0.15.0, so -`PasskeyPolicyErrorCode` in `src/client/errors.ts` is a local copy of the API's -list. If the types package starts exporting one, switch to it so the -`Record` check catches upstream drift the way the OAuth one does. +`@seamless-auth/types` 0.16.0 publishes `WebAuthnErrorCode`, which covers every +machine code the API sends for WebAuthn across all of its operations, so +`PasskeyPolicyErrorCode` in `src/client/errors.ts` is derived from it rather than +kept as a local list. It is that union minus `prf_output_not_allowed`, a `400` +from login and step-up finish that reports a caller which failed to strip PRF +output, not a deployment refusing an authenticator. + +Derive it by subtraction, not by listing the codes you want. A code added +upstream then lands in `PasskeyPolicyErrorCode`, and the +`Record` map stops compiling until someone either +handles it or excludes it deliberately. Listing the wanted names would drop a new +code on the floor and leave the map compiling, which is the silent drift the +union exists to prevent. Before documenting new flow behavior, verify the route contract in `seamless-auth-server` or `seamless-auth-api`. diff --git a/src/client/errors.ts b/src/client/errors.ts index 71172d9..4398856 100644 --- a/src/client/errors.ts +++ b/src/client/errors.ts @@ -93,12 +93,9 @@ export function getOAuthErrorCode(error: unknown): OAuthErrorCode | undefined { * ceremony runs. The rest come from register/finish with a `403`, once the * credential exists and can be inspected. */ -export type PasskeyPolicyErrorCode = Extract< +export type PasskeyPolicyErrorCode = Exclude< WebAuthnErrorCodeShape, - | 'attachment_not_allowed' - | 'synced_passkey_not_allowed' - | 'authenticator_not_allowed' - | 'prf_required' + 'prf_output_not_allowed' >; /* @@ -109,10 +106,12 @@ export type PasskeyPolicyErrorCode = Extract< * refusing an authenticator. Reporting it as a policy refusal would point an * integrator at their configuration for what is a bug in the caller. * - * `Extract` ties these names to the upstream union: if one is renamed or dropped - * there, it resolves to `never` and the `Record` below stops compiling. As with - * the OAuth codes, the runtime list stays out of the browser bundle so Zod does - * not come with it. + * Subtracting that one name, rather than listing the four that are wanted, is + * what makes upstream additions visible: a code added to `WebAuthnErrorCode` + * lands in this type, and the `Record` below then fails to compile until it is + * either handled here or excluded on purpose. Listing the wanted names instead + * would silently ignore it. As with the OAuth codes, the runtime list stays out + * of the browser bundle so Zod does not come with it. */ const PASSKEY_POLICY_ERROR_CODES: Record = { attachment_not_allowed: true,