feat(webauthn): support attestation and validate it against FIDO metadata - #206
Merged
Conversation
…data Registration hardcoded attestationType 'none', so authenticators never identified themselves and there was nothing for the FIDO Metadata Service to validate. FIDO Server Requirements v2.3 requires validating attestation certificate chains and supporting validation through that service. authenticator_policy.attestation now chooses, defaulting to none so consumer deployments are unaffected and nobody pays the privacy cost of identifying a user's hardware without needing to. Under direct the metadata service is prepared at startup and the library's attestation verifiers validate against it. requireKnownAuthenticator decides what happens to an authenticator the metadata service does not list. That was the open positioning question in the readiness analysis, and it maps onto the library's permissive and strict verification modes, so it is exposed as configuration rather than decided here. It defaults to permissive. Credentials record attestationFormat and attestationVerified, so an audit can tell an unattested credential from one whose attestation was checked. The metadata service never blocks startup. A blob that cannot be fetched is a degraded state, not a reason to refuse to start, and requireKnownAuthenticator is deliberately not honoured in that state because refusing every registration on a transient network failure is worse than the risk it guards against. Migration verified up and down against Postgres 17. Closes #168 Closes #169
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 #168 and #169. Requires
@seamless-auth/types0.14.0, already released.Why both in one change
The two issues describe one mechanism, and each says it pairs with the other. It
is not a scope decision so much as a fact about the library: metadata validation
lives inside the attestation verifiers, in the branch that only runs when a
statement with
x5cis present.With
attestationType: 'none'there is no statement, so that branch neverexecutes. #168 alone would have shipped code that provably does nothing, and two
of its three acceptance criteria cannot be met without #169. They also share one
config key and would have had to merge together.
The change
authenticator_policy.attestation,noneordirect.nonestays the default:attestation identifies a user's hardware, which is a real privacy cost, and a
consumer deployment has no use for it. Under
directthe metadata service isprepared at startup and the library's verifiers validate against it.
Credentials record
attestationFormatandattestationVerified, so an audit candistinguish an unattested credential from one whose attestation was actually
checked. Neither is recoverable later, which is why it is captured now.
Open question 1, answered as configuration
The readiness analysis flagged "what happens to an authenticator the metadata
service does not list" as an open positioning decision rather than a technical
one.
It turns out to map exactly onto the library's
verificationMode:permissiveregisters an unlisted authenticator,
strictrefuses it. Rather than pick aposition for every deployment, it is exposed as
requireKnownAuthenticator,defaulting to permissive so an upgrade cannot lock anyone out of their own
authenticator.
The failure posture, which is the part worth arguing with
The metadata service never blocks startup. A blob that cannot be fetched is a
degraded state, not a reason an authentication server should refuse to start. It
is logged and registration continues without metadata validation.
requireKnownAuthenticatoris deliberately not honoured in that state. Thestrict reading would be to refuse every registration when metadata is
unavailable. I judged that worse than the risk it guards against: a transient
network failure at boot would otherwise stop every user in the organisation from
enrolling, which is a self-inflicted outage in exchange for a window during which
an unlisted authenticator could register.
isMetadataServiceReady()reportswhich state the process is in so the degraded case is observable.
This is a security tradeoff rather than an obvious call, and it is the thing to
push back on if you read it differently.
Also worth knowing
Blob refresh needs no scheduling from us.
getStatementcoordinates cacheupdates from the blob's own
nextUpdate, so #168's "MDS blob refresh isscheduled" is satisfied by the library rather than by a cron.
Changing
attestationneeds a restart, since the service is prepared once atboot. Documented in
configuration.mdand.env.example.Verification
93 test files, 1025 passed, 1 skipped. Coverage 98.79% lines.
typecheck,lint,format:checkclean. Migration verified up and down against a realPostgres 17.
Five tests on the bootstrap alone, covering the no-attestation path doing no
network work at all, permissive and strict wiring, an unreachable blob, and
unreadable config. Four more on the controller for requesting attestation and
persisting the outcome.
Not done here
Exercising all four attestation formats end to end, which #169 lists. That needs
real attestation fixtures and is properly part of the conformance run in #172,
where the tools supply them.