Skip to content

fix: skip origin check when --client-id is supplied#6

Open
bowlofarugula wants to merge 3 commits intomasterfrom
fix/client-id-bypasses-origin-check
Open

fix: skip origin check when --client-id is supplied#6
bowlofarugula wants to merge 3 commits intomasterfrom
fix/client-id-bypasses-origin-check

Conversation

@bowlofarugula
Copy link
Copy Markdown

@bowlofarugula bowlofarugula commented May 7, 2026

Summary

_validate_auth_server_origin was supposed to be bypassable via --client-id (the error message said so), but the bypass was never implemented — the check always ran regardless of whether pre-registered credentials were supplied.

  • Root cause: the same-origin guard had no if not client_id: condition
  • Symptom: murl refused to connect to MCP servers where the resource server and auth server are intentionally on different domains — e.g. AWS AgentCore (.amazonaws.com) + Okta (.okta.com)

What changed

  • murl/auth.py: wrap _validate_auth_server_origin call in if not client_id:; when skipped, emit a warning naming the unvalidated issuer URL so the user can verify it's their expected provider
  • murl/auth.py: drop the inaccurate "RFC 9728 §3" citation — §3 defines Protected Resource Metadata format, not origin-matching; the check is a murl heuristic, not a spec mandate
  • murl/auth.py: update the error message in _validate_auth_server_origin to remove the now-accurate "use --client-id to bypass" guidance (which now works)
  • tests/test_auth.py: add regression test for cross-domain auth server + client_id supplied — asserts no OAuthError and that the warning is emitted naming the issuer

Security considerations

The same-origin check prevents a malicious resource server from advertising a foreign AS and redirecting a victim's browser there. Skipping it is a real trade-off:

  • When --client-id is NOT set: The check is enforced. murl rejects any resource server whose authorization_servers list points off-domain.
  • When --client-id IS set: The check is skipped. A compromised resource server could still advertise an attacker-controlled AS. We print a warning naming the issuer — the user's best defense is to verify the domain in the warning (and, when the browser opens, in the address bar) before entering credentials. PKCE + state remain in force and protect against code injection and CSRF but do not protect against credential phishing via a lookalike AS.

The correct fix for deployments that want a stronger guarantee is an explicit --auth-server flag that pins the expected AS and fails if discovery returns something different. That's out of scope for this PR (filed as a follow-up) — but the warning here ensures users are not silently exposed.

Test plan

  • uv run pytest tests/test_auth.py — all 62 tests pass
  • Manual: murl --client-id <id> --client-secret <secret> --callback-port <port> <agentcore-url> no longer fails with "authorization server hostname does not match", and prints a warning naming the Okta issuer

The `_validate_auth_server_origin` check (RFC 9728 §3) blocked servers
where the resource server and authorization server are intentionally on
different domains — e.g. an AWS AgentCore runtime (bedrock-agentcore.
us-east-1.amazonaws.com) backed by Okta (oktapreview.com). The error
message already said "use --client-id to bypass this check" but the code
never implemented that bypass.

When the caller supplies pre-registered credentials via --client-id, they
have already established the trust relationship out-of-band. Skip the
hostname comparison in that case and proceed with discovery normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 06f72aad-50d5-4947-ae19-e54074c3ed49

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This change modifies OAuth flow validation in murl/auth.py to support cross-domain deployments with pre-registered credentials. The authorize() function now conditionally skips hostname origin validation when a client_id is provided. The hostname-mismatch error message was updated to guide users toward supplying --client-id for intentional cross-domain setups. OAuth flow semantics remain unchanged for other scenarios.


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

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@murl/auth.py`:
- Around line 508-514: The code currently skips _validate_auth_server_origin
whenever client_id is present, allowing a malicious resource server-controlled
issuer_url (from discover_auth_server_metadata / auth_servers[0]) to be used
without scrutiny; change authorize() so that when client_id is supplied you no
longer silently bypass validation: emit a clear warning naming the unvalidated
issuer_url (including the raw auth_servers[0] value) and log that DCR was
skipped, and additionally add an optional issuer_url / --auth-server parameter
to authorize()/CLI and, if provided, compare it against the discovered
issuer_url (or fail) instead of relying solely on client_id; keep the existing
_validate_auth_server_origin call when no explicit issuer is provided.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7ef81ee3-182e-402c-935c-df9a9e3059d1

📥 Commits

Reviewing files that changed from the base of the PR and between 4ec36f9 and 2b7e06a.

📒 Files selected for processing (1)
  • murl/auth.py

Comment thread murl/auth.py Outdated
bowlofarugula and others added 2 commits May 7, 2026 17:25
…e supplied

When --client-id is passed the caller has established the trust relationship
out-of-band (e.g. AWS AgentCore + Okta: resource server on *.amazonaws.com,
auth server on *.okta.com). The same-origin heuristic would only block
legitimate cross-domain deployments in this case, since PKCE + state still
provide the attack-surface protection the check was meant for.

Also drops the inaccurate "RFC 9728 §3" citation from the inline comment
(§3 defines Protected Resource Metadata format, not origin-matching).

Adds a regression test for the cross-domain + client_id path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ent_id

A compromised resource server can advertise any authorization_servers URL.
When --client-id bypasses the same-origin check, print a warning naming
the unvalidated issuer so the user can confirm it's their expected provider
before their browser opens. PKCE+state still protect code injection and
CSRF, but naming the issuer is the only guard against phishing via a
malicious redirect.

Also fixes test to assert the warning is emitted and contains the issuer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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