Skip to content

feat(config): let a deployment refuse synced passkeys and restrict models - #49

Merged
Bccorb merged 1 commit into
mainfrom
feat/synced-passkey-and-aaguid-policy
Aug 30, 2026
Merged

Bccorb merged 1 commit into
mainfrom
feat/synced-passkey-and-aaguid-policy

Conversation

@Bccorb

@Bccorb Bccorb commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Unblocks fells-code/seamless-auth-api#170, the last of that repository's attestation and metadata session.

What

AuthenticatorPolicySchema gains syncedPasskeys (allow | block, default block), aaguidAllowList and aaguidDenyList.

The default is a deliberate behaviour change

A multi-device credential is synced by a platform password manager, so its private key exists somewhere outside the authenticator that created it. Every iCloud Keychain and Google Password Manager passkey is one.

Defaulting to block matches the design position stated in the API repository's public sector documentation, which recorded "blocked by default, agency may enable" as the intent and which that repository's own README confirmed was not implemented. This closes that gap in the direction the position states rather than by rewriting the position.

The cost is real and should not be understated: a deployment relying on platform passkeys stops enrolling them on upgrade until it sets syncedPasskeys: 'allow'. The consuming release needs to say so loudly.

Eligibility, not current state

The judgement is on backup eligibility rather than backup state. A credential that can leave the device is the exposure, whether or not it has yet. Judging on current state would let a credential register while unsynced and sync afterwards.

The AAGUID lists depend on attestation

They only mean something under attestation: 'direct'. An authenticator that was never asked to identify itself reports an all-zero AAGUID, so an allow list would reject everything and a deny list would catch nothing. Documented on the fields.

One tidy-up

The whole-object default for authenticator_policy was restated next to the field defaults, which is two places to keep in step. It is now derived from the field defaults, with a test asserting the two agree.

Verification

16 test files, 208 tests, 8 new. typecheck, lint, format:check clean.

…dels

AuthenticatorPolicySchema gains syncedPasskeys, defaulting to block, plus
aaguidAllowList and aaguidDenyList.

A multi-device credential is synced by a platform password manager, so its
private key exists somewhere outside the authenticator that created it. That is
what a consumer wants and what an organisation issuing its own authenticators
does not. Blocking is the default, matching the stated design position, and a
deployment that wants platform passkeys opts in.

Judged on backup eligibility rather than current backup state, because a
credential that can leave the device is the exposure whether or not it already
has.

The AAGUID lists need attestation direct to mean anything, since an
authenticator never asked to identify itself reports no usable AAGUID.

The whole-object default for authenticator_policy is now derived from the field
defaults rather than restated, so the two cannot drift.

Refs fells-code/seamless-auth-api#170
@Bccorb
Bccorb merged commit 0857223 into main Aug 30, 2026
1 check passed
@Bccorb
Bccorb deleted the feat/synced-passkey-and-aaguid-policy branch August 30, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant