Skip to content

feat!: tighten cross-origin defaults#647

Merged
james-d-elliott merged 1 commit into
masterfrom
feat-cross-origin-policy
Apr 19, 2026
Merged

feat!: tighten cross-origin defaults#647
james-d-elliott merged 1 commit into
masterfrom
feat-cross-origin-policy

Conversation

@james-d-elliott
Copy link
Copy Markdown
Member

@james-d-elliott james-d-elliott commented Apr 5, 2026

Cross-Origin WebAuthn ceremonies are now rejected by default.
Integrators that need to accept them must opt in by setting the new
[webauthn.Config.RPAllowCrossOrigin] field to true.

The previously-permissive Top Origin handling has also been tightened.
The zero value of [protocol.TopOriginVerificationMode] is now coerced
by [webauthn.Config.validate] to
[protocol.TopOriginExplicitVerificationMode]; the strictest active
mode, which accepts a Top Origin only if it matches an entry in
[webauthn.Config.RPTopOrigins]. The opt-out mode
protocol.TopOriginIgnoreVerificationMode has been removed entirely;
[protocol.CollectedClientData.Verify] returns an "unknown Top Origin
verification mode" error if it is ever encountered.

Together these changes align the library with the W3C WebAuthn Level 3
guidance that Cross-Origin embedding and Top Origin acceptance should
be explicit relying-party decisions rather than defaults.

BREAKING CHANGE: The Cross-Origin verification semantics have changed
significantly due to the stabilization of the WebAuthn Level 3
specification. It is no longer possible to disable verification, and
Cross-Origin ceremonies must explicitly be allowed in this release.

  • protocol.TopOriginIgnoreVerificationMode has been removed. Code that
    referenced it must switch to one of the other constants as there is
    no longer a mode which disables the Top Origin verification such as:

    • TopOriginExplicitVerificationMode; match against RPTopOrigins only
      (recommended, and the new coerced default)
    • TopOriginAutoVerificationMode; match against the union of
      RPTopOrigins and RPOrigins
    • TopOriginImplicitVerificationMode; match against RPOrigins only
  • webauthn.Config.validate now rewrites a zero-valued
    RPTopOriginVerificationMode to TopOriginExplicitVerificationMode.
    Integrators that left the field unset previously got ignore-mode
    semantics (any Top Origin accepted); they now get strict matching
    against RPTopOrigins and must populate that list, or explicitly
    select a different mode; for Cross-Origin flows to succeed.

  • Cross-Origin ceremonies (those where the authenticator reports
    crossOrigin = true in the ClientData) are rejected by default.
    Integrators that rely on iframe-embedded or other Cross-Origin WebAuthn
    flows must set webauthn.Config.RPAllowCrossOrigin = true. The library
    continues to enforce Top Origin verification on accepted Cross-Origin
    ceremonies per the configured mode.

  • protocol.CollectedClientData.Verify no longer accepts
    TopOriginIgnoreVerificationMode; callers that pass an unknown mode
    receive ErrNotImplemented with detail "unknown Top Origin
    verification mode".

@james-d-elliott james-d-elliott requested a review from a team as a code owner April 5, 2026 12:42
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Threads an explicit RP-level allowCrossOrigin flag through client-data, creation, and assertion verification, enforces cross-origin rejection when disallowed, removes the "ignore" top-origin verification mode, and defaults RP top-origin verification to explicit.

Changes

Cohort / File(s) Summary
Assertion Verification
protocol/assertion.go, protocol/assertion_test.go
ParsedCredentialAssertionData.Verify signature reordered to accept allowCrossOrigin; test call-sites updated and top-origin mode switched to explicit.
Credential Creation Verification
protocol/credential.go, protocol/credential_test.go
ParsedCredentialCreationData.Verify signature reordered to accept allowCrossOrigin; callers/tests updated to pass the flag.
Collected Client Data Verification
protocol/client.go, protocol/client_test.go
CollectedClientData.Verify gains allowCrossOrigin param; rejects when c.CrossOrigin==true && allowCrossOrigin==false; TopOriginIgnoreVerificationMode removed; tests consolidated and updated.
Attestation Tests
protocol/attestation_test.go
pcc.Verify invocations updated for reordered args and explicit top-origin mode.
WebAuthn Integration & Config
webauthn/types.go, webauthn/login.go, webauthn/registration.go
Added Config.RPAllowCrossOrigin (default false); validation now coerces default top-origin mode to explicit; login/registration pass RPAllowCrossOrigin into Verify.
Spec E2E Tests
protocol/specification_vectors_e2e_test.go
E2E vectors extended with allowCrossOrigin; registration/authentication test calls updated and assertions adjusted for cross-origin errors.
Config Tests
webauthn/types_test.go
New test ensuring default/coerced RPTopOriginVerificationMode becomes explicit; imports require.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,230,255,0.5)
    Browser->>Server: Send credential/assertion response (clientData, topOrigin, crossOrigin)
    end
    rect rgba(200,255,200,0.5)
    Server->>Config: Read RPAllowCrossOrigin, RPTopOriginVerificationMode
    end
    rect rgba(255,230,200,0.5)
    Server->>CollectedClientData: Verify(storedChallenge, ceremony, rpOrigins, rpTopOrigins, rpTopOriginMode, allowCrossOrigin)
    Note right of CollectedClientData: if c.CrossOrigin && !allowCrossOrigin -> ErrVerification
    CollectedClientData-->>Server: verification result / clientDataHash
    end
    rect rgba(230,200,255,0.5)
    Server->>ParsedCredential*: Continue creation/assertion verify flows (attestation / signature checks)
    ParsedCredential*-->>Server: final verification result
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through flags and top-origin trails,

A tiny gate for cross-origin tales.
I stitched the flag from client up to core,
Said "no ignore" and tightened the door.
Crunchy carrots of safety—verified once more.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description accurately relates to and describes the changeset, detailing the removal of TopOriginIgnoreVerificationMode, addition of RPAllowCrossOrigin field, and changes to default verification behavior.
Title check ✅ Passed The title clearly and concisely summarizes the main objective: tightening cross-origin defaults by making cross-origin requests prohibited by default and changing the default TopOriginVerificationMode behavior.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-cross-origin-policy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.33%. Comparing base (a100673) to head (18f46fb).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #647      +/-   ##
==========================================
+ Coverage   79.23%   79.33%   +0.09%     
==========================================
  Files          46       46              
  Lines        3145     3150       +5     
==========================================
+ Hits         2492     2499       +7     
+ Misses        461      460       -1     
+ Partials      192      191       -1     
Files with missing lines Coverage Δ
protocol/assertion.go 100.00% <100.00%> (ø)
protocol/client.go 100.00% <100.00%> (ø)
protocol/credential.go 94.59% <100.00%> (+2.70%) ⬆️
webauthn/login.go 94.36% <100.00%> (ø)
webauthn/registration.go 94.93% <100.00%> (ø)
webauthn/types.go 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@james-d-elliott james-d-elliott force-pushed the feat-cross-origin-policy branch 3 times, most recently from a0851f7 to 01aad92 Compare April 5, 2026 12:49
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
protocol/attestation_spec_e2e_test.go (1)

31-42: ⚠️ Potential issue | 🟠 Major

Add a real cross-origin E2E vector.

Lines 39 and 110 add allowCrossOrigin, but every testcase still leaves it false, and the fixtures in this file keep crossOrigin:false. That means lines 91 and 160 only reassert same-origin behavior, so the new policy path is never exercised. Please add at least one passing case with crossOrigin:true + matching topOrigin, and one failing case with either allowCrossOrigin=false or a mismatched topOrigin.

I can help generate the minimal hex-encoded clientDataJSON fixtures if that would save time.

Also applies to: 91-92, 100-111, 160-160

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@protocol/attestation_spec_e2e_test.go` around lines 31 - 42, Test vectors
never exercise the cross-origin path: update TestSpecVectors_Registration_E2E
test cases and associated fixtures to include at least one passing vector with
allowCrossOrigin:true and a clientDataJSON whose topOrigin matches the relying
party (so the cross-origin check succeeds) and one failing vector where
allowCrossOrigin is false or the topOrigin is deliberately mismatched;
specifically add entries in the testCases slice (in
TestSpecVectors_Registration_E2E) with allowCrossOrigin:true and
allowCrossOrigin:false (or mismatched topOrigin) and create corresponding
hex-encoded clientDataJSON fixtures that set the topOrigin field to the intended
origin values so the test exercises both the success and failure branches for
the cross-origin logic.
protocol/client.go (1)

210-213: ⚠️ Potential issue | 🟡 Minor

Stale comment references removed constant TopOriginIgnoreVerificationMode.

The comment states "this mode is the same as TopOriginIgnoreVerificationMode" but this constant was removed in this PR. The comment should be updated to reflect the new behavior.

📝 Suggested fix
	// TopOriginDefaultVerificationMode represents the default verification mode for the Top Origin. At this time this
-	// mode is the same as TopOriginIgnoreVerificationMode until such a time as the specification becomes stable. This
+	// mode is intended as a placeholder until such a time as the specification becomes stable. This
	// value is intended as a fallback value and implementers should very intentionally pick another option if they want
	// stability.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@protocol/client.go` around lines 210 - 213, Update the stale comment for
TopOriginDefaultVerificationMode to remove the reference to the removed constant
TopOriginIgnoreVerificationMode and describe the current behavior instead;
locate the comment adjacent to the TopOriginDefaultVerificationMode declaration
and rephrase it to say this is the fallback/default verification mode for Top
Origin and specify what the mode currently does (e.g., whether verification is
disabled, permissive, or unspecified) so readers don't see a reference to a
removed symbol.
🧹 Nitpick comments (2)
protocol/client_test.go (1)

20-20: Typo in field name: allowCrossOrign should be allowCrossOrigin.

This typo is consistent throughout the test file but reduces readability and creates a naming inconsistency with the production code parameter name allowCrossOrigin.

🔤 Suggested fix
-		allowCrossOrign bool
+		allowCrossOrigin bool

Also update all usages in test cases (lines 33, 40, 47, 55, 65, 73, 82, 91, 102, 113, 135) and the Verify call (line 175) from tc.allowCrossOrign to tc.allowCrossOrigin.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@protocol/client_test.go` at line 20, The test struct and all usages contain a
typo: the field is named allowCrossOrign but should be allowCrossOrigin; rename
the struct field to allowCrossOrigin and update every reference in the test file
(all tc.allowCrossOrign occurrences in the test cases and the Verify call where
tc is used) to tc.allowCrossOrigin so the test names match production parameter
naming and compile correctly.
protocol/client.go (1)

152-154: Consider using if instead of switch for length check.

The switch len(c.TopOrigin) with case 0: break and default: is an unusual pattern. A straightforward if statement would be more idiomatic.

♻️ Suggested refactor
-	switch len(c.TopOrigin) {
-	case 0:
-		break
-	default:
+	if len(c.TopOrigin) > 0 {
		if !c.CrossOrigin {
			return ErrVerification.
				WithDetails("Error validating topOrigin").
				WithInfo("The topOrigin can't have values unless crossOrigin is true.")
		}
		// ... rest of the validation logic ...
+	}
-	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@protocol/client.go` around lines 152 - 154, The switch on len(c.TopOrigin)
(switch len(c.TopOrigin) { case 0: break ... default: ... }) is non-idiomatic;
replace it with a simple if/else using the same symbol names so it’s easier to
read — e.g., check if len(c.TopOrigin) == 0 then handle that branch (or return)
else execute the existing default branch; update the surrounding code in the
same method where c.TopOrigin is used so behavior remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@protocol/assertion.go`:
- Around line 148-155: The Verify method on ParsedCredentialAssertionData
exposes three adjacent bools (allowCrossOrigin, verifyUser, verifyUserPresence)
which is error-prone; replace them with a single options parameter (e.g., type
AssertionVerificationOptions { AllowCrossOrigin bool; VerifyUser bool;
VerifyUserPresence bool }) or add a new method
ParseCredentialAssertionData.VerifyWithOptions(opts
AssertionVerificationOptions) that accepts that struct and leaves Verify as a
thin deprecated shim that calls the new method for backward compatibility;
update all call sites to build and pass the options struct (or use the new
method), and update doc/comments for
ParsedCredentialAssertionData.Verify/VerifyWithOptions to describe the options
and defaults.

In `@webauthn/types.go`:
- Around line 145-146: The Config comment and tests need updating to reflect
that RPTopOriginVerificationMode now defaults to
TopOriginExplicitVerificationMode instead of the ignore/default behavior; update
the Config doc comment above the Config type to describe the new explicit
default and add a regression test (e.g., in types_test.go) that constructs the
config via the public constructor (New(...) or calls validate()) and asserts
that RPTopOriginVerificationMode is set to
protocol.TopOriginExplicitVerificationMode when the zero value or
TopOriginDefaultVerificationMode is provided, preventing future regressions.
Ensure the test references RPTopOriginVerificationMode,
protocol.TopOriginDefaultVerificationMode, and
protocol.TopOriginExplicitVerificationMode and fails if the value differs.

---

Outside diff comments:
In `@protocol/attestation_spec_e2e_test.go`:
- Around line 31-42: Test vectors never exercise the cross-origin path: update
TestSpecVectors_Registration_E2E test cases and associated fixtures to include
at least one passing vector with allowCrossOrigin:true and a clientDataJSON
whose topOrigin matches the relying party (so the cross-origin check succeeds)
and one failing vector where allowCrossOrigin is false or the topOrigin is
deliberately mismatched; specifically add entries in the testCases slice (in
TestSpecVectors_Registration_E2E) with allowCrossOrigin:true and
allowCrossOrigin:false (or mismatched topOrigin) and create corresponding
hex-encoded clientDataJSON fixtures that set the topOrigin field to the intended
origin values so the test exercises both the success and failure branches for
the cross-origin logic.

In `@protocol/client.go`:
- Around line 210-213: Update the stale comment for
TopOriginDefaultVerificationMode to remove the reference to the removed constant
TopOriginIgnoreVerificationMode and describe the current behavior instead;
locate the comment adjacent to the TopOriginDefaultVerificationMode declaration
and rephrase it to say this is the fallback/default verification mode for Top
Origin and specify what the mode currently does (e.g., whether verification is
disabled, permissive, or unspecified) so readers don't see a reference to a
removed symbol.

---

Nitpick comments:
In `@protocol/client_test.go`:
- Line 20: The test struct and all usages contain a typo: the field is named
allowCrossOrign but should be allowCrossOrigin; rename the struct field to
allowCrossOrigin and update every reference in the test file (all
tc.allowCrossOrign occurrences in the test cases and the Verify call where tc is
used) to tc.allowCrossOrigin so the test names match production parameter naming
and compile correctly.

In `@protocol/client.go`:
- Around line 152-154: The switch on len(c.TopOrigin) (switch len(c.TopOrigin) {
case 0: break ... default: ... }) is non-idiomatic; replace it with a simple
if/else using the same symbol names so it’s easier to read — e.g., check if
len(c.TopOrigin) == 0 then handle that branch (or return) else execute the
existing default branch; update the surrounding code in the same method where
c.TopOrigin is used so behavior remains identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7d54ab26-e566-486c-b1ca-3c53c62e9e6a

📥 Commits

Reviewing files that changed from the base of the PR and between a1c438f and f3ca153.

📒 Files selected for processing (11)
  • protocol/assertion.go
  • protocol/assertion_test.go
  • protocol/attestation_spec_e2e_test.go
  • protocol/attestation_test.go
  • protocol/client.go
  • protocol/client_test.go
  • protocol/credential.go
  • protocol/credential_test.go
  • webauthn/login.go
  • webauthn/registration.go
  • webauthn/types.go

Comment thread protocol/assertion.go Outdated
Comment thread webauthn/types.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
protocol/client.go (1)

152-180: Consider simplifying the switch to an if-else.

The switch len(c.TopOrigin) with only case 0: and default: branches could be expressed more idiomatically as an if statement. Additionally, the break is unnecessary in Go since switch cases don't fall through by default.

♻️ Suggested simplification
-	switch len(c.TopOrigin) {
-	case 0:
-		break
-	default:
+	if len(c.TopOrigin) != 0 {
 		if !c.CrossOrigin {
 			return ErrVerification.
 				WithDetails("Error validating topOrigin").
 				WithInfo("The topOrigin can't have values unless crossOrigin is true.")
 		}

 		var possibleTopOrigins []string

 		switch rpTopOriginsVerify {
 		case TopOriginExplicitVerificationMode:
 			possibleTopOrigins = rpTopOrigins
 		case TopOriginAutoVerificationMode:
 			possibleTopOrigins = append(rpTopOrigins, rpOrigins...) //nolint:gocritic // This is intentional.
 		case TopOriginImplicitVerificationMode:
 			possibleTopOrigins = rpOrigins
 		default:
 			return ErrNotImplemented.WithDetails("Error handling unknown Top Origin verification mode")
 		}

 		if !IsOriginInHaystack(c.TopOrigin, possibleTopOrigins) {
 			return ErrVerification.
 				WithDetails("Error validating top origin").
 				WithInfo(fmt.Sprintf("Expected Values: %s, Received: %s", possibleTopOrigins, c.TopOrigin))
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@protocol/client.go` around lines 152 - 180, The switch on len(c.TopOrigin) is
unnecessarily verbose; replace the switch block with a simple if
len(c.TopOrigin) == 0 { /* no-op */ } else { ... } (or more idiomatically if
len(c.TopOrigin) != 0 { ... }) to handle the non-empty top origin path. Keep the
existing logic inside the non-empty branch: check c.CrossOrigin and return
ErrVerification with details if false, compute possibleTopOrigins based on
rpTopOriginsVerify (TopOriginExplicitVerificationMode,
TopOriginAutoVerificationMode, TopOriginImplicitVerificationMode) and return
ErrNotImplemented for the default case, then call
IsOriginInHaystack(c.TopOrigin, possibleTopOrigins) and return ErrVerification
with formatted info if it returns false; preserve all referenced symbols
(c.TopOrigin, c.CrossOrigin, rpTopOriginsVerify, rpTopOrigins, rpOrigins,
possibleTopOrigins, IsOriginInHaystack, ErrVerification, ErrNotImplemented).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@protocol/client.go`:
- Around line 152-180: The switch on len(c.TopOrigin) is unnecessarily verbose;
replace the switch block with a simple if len(c.TopOrigin) == 0 { /* no-op */ }
else { ... } (or more idiomatically if len(c.TopOrigin) != 0 { ... }) to handle
the non-empty top origin path. Keep the existing logic inside the non-empty
branch: check c.CrossOrigin and return ErrVerification with details if false,
compute possibleTopOrigins based on rpTopOriginsVerify
(TopOriginExplicitVerificationMode, TopOriginAutoVerificationMode,
TopOriginImplicitVerificationMode) and return ErrNotImplemented for the default
case, then call IsOriginInHaystack(c.TopOrigin, possibleTopOrigins) and return
ErrVerification with formatted info if it returns false; preserve all referenced
symbols (c.TopOrigin, c.CrossOrigin, rpTopOriginsVerify, rpTopOrigins,
rpOrigins, possibleTopOrigins, IsOriginInHaystack, ErrVerification,
ErrNotImplemented).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e8ee3ab0-6f59-4c3f-bb4a-4eb5504a5833

📥 Commits

Reviewing files that changed from the base of the PR and between f3ca153 and 01aad92.

📒 Files selected for processing (11)
  • protocol/assertion.go
  • protocol/assertion_test.go
  • protocol/attestation_spec_e2e_test.go
  • protocol/attestation_test.go
  • protocol/client.go
  • protocol/client_test.go
  • protocol/credential.go
  • protocol/credential_test.go
  • webauthn/login.go
  • webauthn/registration.go
  • webauthn/types.go
✅ Files skipped from review due to trivial changes (2)
  • webauthn/registration.go
  • protocol/attestation_spec_e2e_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • protocol/attestation_test.go
  • protocol/credential_test.go
  • webauthn/types.go
  • webauthn/login.go
  • protocol/assertion.go
  • protocol/credential.go
  • protocol/client_test.go

@james-d-elliott james-d-elliott force-pushed the feat-cross-origin-policy branch from 01aad92 to 287c48d Compare April 9, 2026 23:10
@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented Apr 9, 2026

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
29652279 Triggered Generic High Entropy Secret 18f46fb protocol/specification_vectors_e2e_test.go View secret
29652279 Triggered Generic High Entropy Secret 18f46fb protocol/specification_vectors_e2e_test.go View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
webauthn/registration.go (1)

95-98: ⚠️ Potential issue | 🟠 Major

Use session.RelyingPartyID for finish-time verification.

BeginRegistration persists the exact RP ID used for this ceremony in SessionData.RelyingPartyID, but Line 146 still verifies against webauthn.Config.RPID. If a functional option overrides the RP ID, or the config changes between begin and finish, valid registrations will be checked against the wrong RP ID.

💡 Minimal fix
-	if clientDataHash, err = parsedResponse.Verify(session.Challenge, webauthn.Config.RPID, webauthn.Config.RPOrigins, webauthn.Config.RPTopOrigins, webauthn.Config.RPTopOriginVerificationMode, webauthn.Config.RPAllowCrossOrigin, shouldVerifyUser, shouldVerifyUserPresence, webauthn.Config.MDS, session.CredParams); err != nil {
+	if clientDataHash, err = parsedResponse.Verify(session.Challenge, session.RelyingPartyID, webauthn.Config.RPOrigins, webauthn.Config.RPTopOrigins, webauthn.Config.RPTopOriginVerificationMode, webauthn.Config.RPAllowCrossOrigin, shouldVerifyUser, shouldVerifyUserPresence, webauthn.Config.MDS, session.CredParams); err != nil {

Also applies to: 146-146

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webauthn/registration.go` around lines 95 - 98, BeginRegistration stores the
exact RP ID in SessionData.RelyingPartyID but FinishRegistration still uses
webauthn.Config.RPID for verification; change the finish-time verification to
use session.RelyingPartyID instead of webauthn.Config.RPID. Locate where
FinishRegistration (or the code that calls webauthn.FinishRegistration /
verifies the attestation) passes the RPID or verification options and replace
the hardcoded webauthn.Config.RPID with session.RelyingPartyID (or pass
session.RelyingPartyID into the verification options) so the same RP ID saved in
SessionData is used for verification.
webauthn/login.go (1)

125-127: ⚠️ Potential issue | 🟠 Major

Validate assertions against session.RelyingPartyID, not the current config.

BeginLogin saves the RP ID actually sent to the client in SessionData.RelyingPartyID, but this path still feeds webauthn.Config.RPID into parsedResponse.Verify. That makes valid assertions fail if the ceremony overrode the RP ID or the config changed after the session was created.

💡 Minimal fix
-	rpID := webauthn.Config.RPID
+	rpID := session.RelyingPartyID

Also applies to: 357-366

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webauthn/login.go` around lines 125 - 127, The verification is using the
current config RPID instead of the RP ID stored on the session
(SessionData.RelyingPartyID), so assertions created under an overridden RP ID or
with a changed config will fail; update the call sites that call
parsedResponse.Verify (and any other verification that pass
webauthn.Config.RPID) to pass the RP ID from the session
(session.RelyingPartyID) instead of webauthn.Config.RPID—look for uses in the
login flow including the block creating SessionData (where Challenge and
RelyingPartyID are set) and the similar verification block around the 357-366
region and replace the config reference with session.RelyingPartyID.
♻️ Duplicate comments (1)
webauthn/types.go (1)

54-57: ⚠️ Potential issue | 🟡 Minor

Update the RPTopOriginVerificationMode docstring.

Line 146 now defaults to protocol.TopOriginExplicitVerificationMode, but the exported comment still says ignore-mode is the default. That leaves the public API docs wrong for a security-sensitive setting.

Also applies to: 145-146

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webauthn/types.go` around lines 54 - 57, The doc comment for
RPTopOriginVerificationMode is stale (it says TopOriginIgnoreVerificationMode is
the default) — update the exported comment for RPTopOriginVerificationMode to
state that the default is now protocol.TopOriginExplicitVerificationMode (or
explain the new behavior) and note that implementers should set it explicitly if
they need a different mode; ensure the comment text mentions the exact symbol
protocol.TopOriginExplicitVerificationMode so generated docs reflect the current
default.
🧹 Nitpick comments (1)
protocol/specification_vectors_e2e_test.go (1)

363-390: Give the allowed and denied top-origin auth cases distinct names.

Both subtests use NoneES256TopOrigin, so go test will auto-suffix one of them and make failures less obvious to triage.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@protocol/specification_vectors_e2e_test.go` around lines 363 - 390, The two
test cases share the same name value "NoneES256TopOrigin", causing go test to
auto-suffix one; update the name field on each test vector to unique,
descriptive values (e.g., "NoneES256TopOrigin_DenyCrossOrigin" for the case with
allowCrossOrigin: false and "NoneES256TopOrigin_AllowCrossOrigin" for the case
with allowCrossOrigin: true) so failures are unambiguous—modify the name field
in the test entries that include rpTopOriginVerificationMode
(TopOriginExplicitVerificationMode) and allowCrossOrigin to reflect the
different behaviors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@webauthn/login.go`:
- Around line 125-127: The verification is using the current config RPID instead
of the RP ID stored on the session (SessionData.RelyingPartyID), so assertions
created under an overridden RP ID or with a changed config will fail; update the
call sites that call parsedResponse.Verify (and any other verification that pass
webauthn.Config.RPID) to pass the RP ID from the session
(session.RelyingPartyID) instead of webauthn.Config.RPID—look for uses in the
login flow including the block creating SessionData (where Challenge and
RelyingPartyID are set) and the similar verification block around the 357-366
region and replace the config reference with session.RelyingPartyID.

In `@webauthn/registration.go`:
- Around line 95-98: BeginRegistration stores the exact RP ID in
SessionData.RelyingPartyID but FinishRegistration still uses
webauthn.Config.RPID for verification; change the finish-time verification to
use session.RelyingPartyID instead of webauthn.Config.RPID. Locate where
FinishRegistration (or the code that calls webauthn.FinishRegistration /
verifies the attestation) passes the RPID or verification options and replace
the hardcoded webauthn.Config.RPID with session.RelyingPartyID (or pass
session.RelyingPartyID into the verification options) so the same RP ID saved in
SessionData is used for verification.

---

Duplicate comments:
In `@webauthn/types.go`:
- Around line 54-57: The doc comment for RPTopOriginVerificationMode is stale
(it says TopOriginIgnoreVerificationMode is the default) — update the exported
comment for RPTopOriginVerificationMode to state that the default is now
protocol.TopOriginExplicitVerificationMode (or explain the new behavior) and
note that implementers should set it explicitly if they need a different mode;
ensure the comment text mentions the exact symbol
protocol.TopOriginExplicitVerificationMode so generated docs reflect the current
default.

---

Nitpick comments:
In `@protocol/specification_vectors_e2e_test.go`:
- Around line 363-390: The two test cases share the same name value
"NoneES256TopOrigin", causing go test to auto-suffix one; update the name field
on each test vector to unique, descriptive values (e.g.,
"NoneES256TopOrigin_DenyCrossOrigin" for the case with allowCrossOrigin: false
and "NoneES256TopOrigin_AllowCrossOrigin" for the case with allowCrossOrigin:
true) so failures are unambiguous—modify the name field in the test entries that
include rpTopOriginVerificationMode (TopOriginExplicitVerificationMode) and
allowCrossOrigin to reflect the different behaviors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cdd7d4d4-59de-464d-b484-e4d8fdb6212a

📥 Commits

Reviewing files that changed from the base of the PR and between 01aad92 and 287c48d.

📒 Files selected for processing (11)
  • protocol/assertion.go
  • protocol/assertion_test.go
  • protocol/attestation_test.go
  • protocol/client.go
  • protocol/client_test.go
  • protocol/credential.go
  • protocol/credential_test.go
  • protocol/specification_vectors_e2e_test.go
  • webauthn/login.go
  • webauthn/registration.go
  • webauthn/types.go
✅ Files skipped from review due to trivial changes (1)
  • protocol/assertion_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • protocol/attestation_test.go
  • protocol/credential.go
  • protocol/credential_test.go

@james-d-elliott james-d-elliott force-pushed the feat-cross-origin-policy branch from 287c48d to 8dcde35 Compare April 19, 2026 05:53
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@protocol/client_test.go`:
- Around line 188-197: The test constructs collected client data with
crossOrigin=true via setupCollectedClientData but calls ccd.Verify(..., false)
so the verify call disallows cross-origin and could cause a cross-origin failure
before the intended challenge mismatch assertion; change the Verify invocation
in TestVerifyCollectedClientData_IncorrectChallenge to pass true for the
allowCrossOrigin parameter (i.e., call ccd.Verify(...,
TopOriginExplicitVerificationMode, true)) so the test isolates and asserts the
challenge mismatch as intended.

In `@protocol/client.go`:
- Line 168: The exported documentation for the TopOrigin mode is out of sync
with the implementation: remove references to the deleted
TopOriginIgnoreVerificationMode and update the TopOrigin auto mode docs to state
that "auto" behaves as the union of RPTopOrigins and RPOrigins (matching the
implementation where possibleTopOrigins = append(rpTopOrigins, rpOrigins...)),
not a fallback-only behavior; edit the exported TopOrigin mode doc block and the
comments around the TopOrigin constants/variables (the doc text covering lines
~210-218) to accurately describe current behavior and remove the stale reference
to the removed mode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5ddba587-3366-42c9-a6b0-c1fa943d71e7

📥 Commits

Reviewing files that changed from the base of the PR and between 287c48d and 8dcde35.

📒 Files selected for processing (12)
  • protocol/assertion.go
  • protocol/assertion_test.go
  • protocol/attestation_test.go
  • protocol/client.go
  • protocol/client_test.go
  • protocol/credential.go
  • protocol/credential_test.go
  • protocol/specification_vectors_e2e_test.go
  • webauthn/login.go
  • webauthn/registration.go
  • webauthn/types.go
  • webauthn/types_test.go
✅ Files skipped from review due to trivial changes (1)
  • protocol/attestation_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • protocol/assertion_test.go
  • protocol/assertion.go
  • protocol/credential_test.go
  • protocol/specification_vectors_e2e_test.go

Comment thread protocol/client_test.go Outdated
Comment thread protocol/client.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@protocol/client.go`:
- Around line 162-168: The switch branch for TopOriginAutoVerificationMode
mutates rpTopOrigins via append(rpTopOrigins, rpOrigins...) which can alter its
backing array; change it to create an independent slice for possibleTopOrigins
(capacity len(rpTopOrigins)+len(rpOrigins)), copy rpTopOrigins into it, then
copy/append rpOrigins into that new slice so rpTopOrigins and rpOrigins are not
modified—update the code paths in the switch that set possibleTopOrigins (and
any uses of possibleTopOrigins) accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cdeb43a4-5188-42b5-ae35-6d9f5b9af8d3

📥 Commits

Reviewing files that changed from the base of the PR and between 8dcde35 and fa22d3b.

📒 Files selected for processing (1)
  • protocol/client.go

Comment thread protocol/client.go Outdated
@james-d-elliott james-d-elliott changed the title feat(protocol): cross origin policy feat!(protocol): cross origin policy Apr 19, 2026
@james-d-elliott james-d-elliott changed the title feat!(protocol): cross origin policy feat(protocol)!: cross origin policy Apr 19, 2026
@james-d-elliott james-d-elliott force-pushed the feat-cross-origin-policy branch from a3c4eda to d446583 Compare April 19, 2026 09:39
@james-d-elliott james-d-elliott changed the title feat(protocol)!: cross origin policy feat!: tighten Cross-Origin defaults Apr 19, 2026
@james-d-elliott james-d-elliott changed the title feat!: tighten Cross-Origin defaults feat!: tighten cross-origin defaults Apr 19, 2026
Cross-Origin WebAuthn ceremonies are now rejected by default.
Integrators that need to accept them must opt in by setting the new
[webauthn.Config.RPAllowCrossOrigin] field to true.

The previously-permissive Top Origin handling has also been tightened.
The zero value of [protocol.TopOriginVerificationMode] is now coerced
by [webauthn.Config.validate] to
[protocol.TopOriginExplicitVerificationMode]; the strictest active
mode, which accepts a Top Origin only if it matches an entry in
[webauthn.Config.RPTopOrigins]. The opt-out mode
protocol.TopOriginIgnoreVerificationMode has been removed entirely;
[protocol.CollectedClientData.Verify] returns an "unknown Top Origin
verification mode" error if it is ever encountered.

Together these changes align the library with the W3C WebAuthn Level 3
guidance that Cross-Origin embedding and Top Origin acceptance should
be explicit relying-party decisions rather than defaults.

BREAKING CHANGE: The Cross-Origin verification semantics have changed
significantly due to the stabilization of the WebAuthn Level 3
specification. It is no longer possible to disable verification, and
Cross-Origin ceremonies must explicitly be allowed in this release.

- protocol.TopOriginIgnoreVerificationMode has been removed. Code that
  referenced it must switch to one of the other constants as there is
  no longer a mode which disables the Top Origin verification such as:
    - TopOriginExplicitVerificationMode; match against RPTopOrigins only
      (recommended, and the new coerced default)
    - TopOriginAutoVerificationMode; match against the union of
      RPTopOrigins and RPOrigins
    - TopOriginImplicitVerificationMode; match against RPOrigins only

- webauthn.Config.validate now rewrites a zero-valued
  RPTopOriginVerificationMode to TopOriginExplicitVerificationMode.
  Integrators that left the field unset previously got ignore-mode
  semantics (any Top Origin accepted); they now get strict matching
  against RPTopOrigins and must populate that list, or explicitly
  select a different mode; for Cross-Origin flows to succeed.

- Cross-Origin ceremonies (those where the authenticator reports
  crossOrigin = true in the ClientData) are rejected by default.
  Integrators that rely on iframe-embedded or other Cross-Origin WebAuthn
  flows must set webauthn.Config.RPAllowCrossOrigin = true. The library
  continues to enforce Top Origin verification on accepted Cross-Origin
  ceremonies per the configured mode.

- protocol.CollectedClientData.Verify no longer accepts
  TopOriginIgnoreVerificationMode; callers that pass an unknown mode
  receive ErrNotImplemented with detail "unknown Top Origin
  verification mode".
@james-d-elliott james-d-elliott force-pushed the feat-cross-origin-policy branch from d446583 to 18f46fb Compare April 19, 2026 09:42
@james-d-elliott james-d-elliott merged commit 80cc224 into master Apr 19, 2026
11 checks passed
@james-d-elliott james-d-elliott deleted the feat-cross-origin-policy branch April 19, 2026 09:49
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