Skip to content

feat(webauthn): related-origins validation (WebAuthn L3 §5.11)#219

Merged
AlfioEmanueleFresta merged 29 commits into
masterfrom
issue-160-related-origins
May 30, 2026
Merged

feat(webauthn): related-origins validation (WebAuthn L3 §5.11)#219
AlfioEmanueleFresta merged 29 commits into
masterfrom
issue-160-related-origins

Conversation

@AlfioEmanueleFresta
Copy link
Copy Markdown
Member

@AlfioEmanueleFresta AlfioEmanueleFresta commented May 17, 2026

Implements WebAuthn L3 §5.11 "Related Origins". When a request's rp.id is not a registrable suffix of the caller's effective domain, libwebauthn resolves the relying party's allowed origins and accepts the request if one matches the caller.

Origin resolution is pluggable and the matching always runs in libwebauthn. An optional reqwest-backed source fetches the RP's .well-known/webauthn document behind the reqwest-related-origins-source feature, so the core crate stays HTTP-client-free. A caller that already has the list, such as a browser, can supply its own source and skip the fetch. Related origins can also be turned off.

Request building now takes its public-suffix-list and related-origins dependencies through a settings value rather than positional parameters, which keeps the API manageable as options grow. Existing call sites need a small update for the new signature.

Closes #160. Based on #173 by @HarveyOrourke15.

@AlfioEmanueleFresta AlfioEmanueleFresta changed the base branch from master to issue-210-dafsa-psl May 17, 2026 18:15
Base automatically changed from issue-210-dafsa-psl to master May 18, 2026 18:42
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the issue-160-related-origins branch from 05f055e to 2a4d4e5 Compare May 18, 2026 19:08
@AlfioEmanueleFresta AlfioEmanueleFresta marked this pull request as ready for review May 18, 2026 19:08
Copy link
Copy Markdown
Collaborator

@msirringhaus msirringhaus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good overall. One architectural question, but I'm fine with the way it is currently, too.

Comment thread libwebauthn/src/ops/webauthn/related_origins/mod.rs Outdated
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the issue-160-related-origins branch from 2a4d4e5 to 23a6d4e Compare May 30, 2026 10:04
…nt arg

Adds the http parameter to FromIdlModel::from_idl_model and the
default WebAuthnIDL::from_json; updates the make_credential and
get_assertion impls to take it (currently unused, wired in next
commit). Updates ceremony examples and existing from_json tests to
pass &NoRelatedOriginsClient and .await the calls.
…d get_assertion

When the rp.id is not a registrable suffix of the caller's effective
domain, call validate_related_origins() per WebAuthn L3 §5.11.1. On
success, accept the request; on failure, surface the existing
MismatchingRelyingPartyId variant unchanged so callers' pattern
matches keep working.
reqwest's referer() defaults to true, so on any redirect chain it would
auto-populate Referer with the previous URL, leaking the RP's well-known
URL to the redirect target. The previous empty-valued Referer default
header did not disable this. Drop the header insertion and call
.referer(false) so no Referer is sent on the initial request or any
redirect, matching WebAuthn L3 \xc2\xa75.11.1 step 2 ("without a referrer").
Extend the trait doc to bind impls to status-200-only and unmodified
Content-Type reporting, so a third-party client cannot accidentally feed
a 404 body or a synthesised application/json type to the validator.
The cap is hard-coded inside the validator loop, so external callers
cannot override it. Reduce to a private const and drop the re-export to
avoid committing to the value across breaking changes.
The previous warn!(error = ?err, ...) debug-printed RelatedOriginsError,
which can carry reqwest error text (IP/port) and serde_json text (body
snippets). Add RelatedOriginsError::kind() that returns a static
discriminant and log only that. Downgrade to debug! since most RPs do
not host /.well-known/webauthn and the failure is expected noise.
The previous impl re-parsed the listed URL through Origin::parse, which
rejects userinfo, non-/ paths, queries and fragments. WebAuthn L3
\xc2\xa75.11.1 step 4.f defers to HTML \xc2\xa77.5 same-origin, which compares only
scheme, host and port. Compare those three directly so a listed entry
like "https://example.com/foo" can match the caller. Add a test.
Symmetric to label_cap_blocks_sixth_distinct_label_match. Asserts that
the 5th distinct label still satisfies step 4.e's size < max check, so
an off-by-one regression on the cap is caught by tests.
The body asserts that an IPv6 listed entry is silently skipped at step
4.c/4.d (no registrable label), not that same-origin matches. Rename to
match and rephrase the comment.
…d_origins

Mirror NoRelatedOriginsClient's placement: under the same feature gate,
expose ReqwestRelatedOriginsClient (and HttpPolicy) at the related_origins
module root so consumers do not need the http:: submodule path.
…iginsClient

Using NoRelatedOriginsClient in the bundled examples taught readers the
wrong default. Wire up the reqwest-backed convenience client instead,
gate the three webauthn ceremony examples on the related-origins-client
feature, and update the README run commands.

Also re-exports HttpPolicy and ReqwestRelatedOriginsClient at
ops::webauthn so examples import from a single path.
…egration test

Swap brand.com/app.brand.org for example.org/app.example.com. RFC 2606
reserves example.* for documentation, so it cannot accidentally collide
with a real party. The two-eTLD shape that exercises the related-origins
fetch path is preserved.
…nFetchError

The trait's old return type was the full RelatedOriginsError, but four of
its five variants (UnexpectedContentType, MalformedJson, MalformedDocument,
NoMatchingOrigin) are produced inside validate_related_origins after the
fetch returns. Implementers had no reason to ever emit them.

Introduce WellKnownFetchError with the variants a fetcher can actually
emit (Transport, Status, BodyTooLarge, NotSupported) and let
RelatedOriginsError wrap it via a Fetch variant with #[from]. The reqwest
client now distinguishes non-200 status from transport faults and from
body-cap hits without stringifying everything.

Also drops RelatedOriginsError::kind(); the two debug! call sites switch
to logging the Display form of the error directly.
AlfioEmanueleFresta and others added 7 commits May 30, 2026 17:36
Adds a short module-level overview to the crate root and each top-level
module. Each one is one or two paragraphs describing the module's
purpose and its main public types, so the docs.rs landing page and every
module page open with context instead of a bare list of items.

This is documentation only, with no code changes.
@AlfioEmanueleFresta AlfioEmanueleFresta force-pushed the issue-160-related-origins branch from ace0a9c to fcfce6b Compare May 30, 2026 16:43
@AlfioEmanueleFresta AlfioEmanueleFresta merged commit 7d26b98 into master May 30, 2026
6 checks passed
@AlfioEmanueleFresta AlfioEmanueleFresta deleted the issue-160-related-origins branch May 30, 2026 17:16
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.

Implement Related Origins support (WebAuthn L3 § 5.11)

2 participants