Skip to content

feat(cua-driver): add experimental MCP OAuth front door - #2063

Open
outdog-hwh wants to merge 2 commits into
trycua:mainfrom
outdog-hwh:codex/mcp-oauth-transparent-proxy-clean
Open

feat(cua-driver): add experimental MCP OAuth front door#2063
outdog-hwh wants to merge 2 commits into
trycua:mainfrom
outdog-hwh:codex/mcp-oauth-transparent-proxy-clean

Conversation

@outdog-hwh

@outdog-hwh outdog-hwh commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

What changed

Adds an experimental cua-driver mcp-oauth subcommand that serves as an OAuth/DCR front door for an existing local MCP HTTP endpoint.

The new entry point:

  • exposes OAuth protected-resource and authorization-server discovery metadata
  • supports Dynamic Client Registration, authorization code + PKCE, and token exchange
  • stores OAuth clients/codes/tokens in a local JSON store with atomic writes
  • validates Bearer tokens and binds token audience/resource to <public-url>/mcp
  • forwards authenticated /mcp requests to a loopback-only upstream such as http://127.0.0.1:7677/mcp
  • preserves streaming proxy behavior for non-tools/list responses
  • returns a connector-friendly tools/list descriptor view while leaving tools/call and other MCP JSON-RPC methods untouched

This does not change the existing cua-driver mcp, serve, or mcp_http behavior.

Why

Some OAuth-only MCP clients require OAuth discovery and Dynamic Client Registration before they will call an MCP endpoint. This keeps that compatibility layer opt-in and isolated from the existing local stdio/HTTP MCP transports.

Safety notes

  • --public-url must be HTTPS.
  • --mcp-upstream is restricted to loopback HTTP hosts.
  • The proxy forwards only selected MCP request headers to the upstream and strips the front-door Bearer token.
  • Clients should be configured with the full MCP endpoint, e.g. https://your-tunnel.example/mcp.

Testing

  • cargo check --offline -p cua-driver
  • cargo test --offline -p cua-driver mcp_oauth -- --nocapture
  • cargo test --offline -p cua-driver parse_positive_u64_accepts_positive_and_rejects_zero_or_invalid -- --nocapture
  • cargo test --offline -p cua-driver manifest_has_documented_top_level_shape -- --nocapture
  • rustfmt --edition 2021 --check crates/cua-driver/src/mcp_oauth.rs
  • git diff --check

Manual acceptance:

  • Started local MCP upstream on 127.0.0.1:7677.
  • Exposed mcp-oauth through a Tailscale HTTPS URL.
  • Connected ChatGPT web to the /mcp URL.
  • Refreshed actions successfully and confirmed the connector can inspect the desktop via MCP tools.

Summary by CodeRabbit

  • New Features

    • Added an experimental OAuth connector bridge for cua-driver, enabling an opt-in HTTPS front door for MCP HTTP access.
    • Added a new CLI command to start the bridge with configurable public URL, listener, upstream endpoint, and token/code lifetimes.
    • Support includes OAuth discovery, client registration, authorization, token issuance, and protected MCP request forwarding.
  • Documentation

    • Expanded the README with setup, compatibility, and operational guidance for the experimental bridge.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

@outdog-hwh is attempting to deploy a commit to the Cua Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

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: 37583ab6-bc20-4cf1-8c50-9ea62b89557a

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

Walkthrough

Adds an experimental mcp-oauth command, wires it into the binary, and introduces a local OAuth front door with discovery, registration, authorization-code exchange, bearer-gated MCP forwarding, tools/list rewriting, and updated docs/tests.

Changes

Experimental OAuth connector bridge

Layer / File(s) Summary
Command surface, docs, and manifest
libs/cua-driver/README.md, libs/cua-driver/rust/crates/cua-driver/src/cli.rs
mcp-oauth is added to the README, CLI parser/help, manifest, telemetry mapping, and positive-TTL validation.
Binary dispatch
libs/cua-driver/rust/crates/cua-driver/src/main.rs
main declares mcp_oauth and routes cli::Command::McpOauth through mcp_oauth::run in both entry points.
Server bootstrap and storage
libs/cua-driver/rust/crates/cua-driver/Cargo.toml, libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
ring is added with the Options, server startup, accept loop, JSON store, HTTP I/O, and shared parsing/validation helpers.
OAuth endpoints and token issuance
libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
Discovery, client registration, authorization, code issuance, and token exchange handlers are added.
Bearer validation and MCP proxying
libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
Bearer token checks, /mcp forwarding, tools/list rewriting, and upstream request/response plumbing are added.
Integration tests
libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
End-to-end tests cover discovery, registration, authorization, token issuance, proxy forwarding, streaming, rewriting, and bearer failures.

Sequence Diagram(s)

sequenceDiagram
  participant OAuthClient
  participant mcp_oauth as cua-driver mcp-oauth
  participant JSONStore as JSON-backed store
  participant LoopbackMCPUpstream as Loopback MCP upstream

  OAuthClient->>mcp_oauth: GET /authorize
  mcp_oauth->>JSONStore: store pending consent / code
  OAuthClient->>mcp_oauth: POST /token
  mcp_oauth->>JSONStore: redeem authorization code
  OAuthClient->>mcp_oauth: POST /mcp with Bearer token
  mcp_oauth->>LoopbackMCPUpstream: forward MCP request
  LoopbackMCPUpstream-->>mcp_oauth: upstream response
  mcp_oauth-->>OAuthClient: proxied response
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Possibly related PRs

  • trycua/cua#1532: Updates telemetry_entry_event(...) to emit the cua_driver_mcp_oauth event name, matching the current CLI telemetry addition.

Poem

I hopped through OAuth lanes tonight,
With carrots, codes, and tokens bright.
The bridge hums soft on loopback dew,
And tools/list sparkles fresh and new.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: an experimental MCP OAuth front door added to cua-driver.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 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 `@libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs`:
- Around line 631-640: The denial branch in handle_authorize_post currently
redirects using form-provided redirect_uri and state before validating the
consent transaction, which leaves the /authorize flow open to redirect abuse.
Update the nonce/pending lookup to happen before any denial redirect, require a
valid pending consent transaction, and use pending.redirect_uri plus
pending.state for the redirect instead of values from the submitted form; also
ensure the registered redirect URI is validated before constructing the Location
header.
- Around line 1092-1097: The read_upstream_body helper currently checks
MAX_HTTP_BODY_BYTES only after read_to_end finishes, so it can allocate too much
before rejecting. Update read_upstream_body to read through a limited reader
capped at MAX_HTTP_BODY_BYTES + 1, and fail as soon as the cap is exceeded; keep
the change localized to this function and preserve the existing timeout/error
handling.
- Around line 958-978: The buffered `tools_list` handling in `mcp_oauth.rs` is
treating chunked upstream bodies as plain bytes, so
`rewrite_tools_list_response`, `proxy_http_response`, and the
`is_tools_list_request` branch can forward chunk-framed data with a mismatched
`Content-Length`. Update the `read_upstream_body`/`proxy_http_response` flow to
either decode chunked `tools/list` responses before rewriting or bypass
buffering and stream them while preserving the original framing, and apply the
same fix in the later `tools/list` path referenced by the comment.
- Around line 352-360: The TTL checks in the OAuth token/consent cleanup path
can overflow when adding expiration offsets, so update the arithmetic in
prune_tokens and prune_consents to use saturating addition instead of direct +
with now_secs(), code_ttl_seconds, or token TTL values. Also apply the same
saturating TTL handling in the related token/consent creation logic in
mcp_oauth.rs so all expiration calculations remain safe for near-u64::MAX
values.
- Around line 1585-1587: The OAuth persistence helpers currently only create the
state directory, but the directory and temp files still inherit default
permissions, so private data can be exposed. Update ensure_private_dir and the
temp-file write/rename flow in mcp_oauth.rs so the OAuth state directory is
created with restrictive access and the temporary files used for client secrets,
authorization codes, and access tokens are explicitly locked down before being
renamed. Make the fix in the same helpers that handle the fs::create_dir_all and
fs::write paths, and ensure the final on-disk files remain private after the
rename.
- Around line 294-431: The store mutation paths in mcp_oauth.rs are performing
load/modify/write without any shared process-local synchronization, which allows
concurrent calls to race and lose updates or redeem the same authorization code
more than once. Add a mutex-protected critical section around every
read-modify-write helper, especially `redeem_code_for_token`, and apply the same
locking to `upsert_client`, `upsert_code`, `upsert_token`, `upsert_consent`, and
`consume_consent` so the loaded map, mutation, and `write_json_map` happen
atomically.
- Around line 593-605: The registration and token responses in mcp_oauth should
not be cacheable because they include secrets and bearer tokens. Update the
response-building paths that call json_response for client registration and the
/token flow so they also set Cache-Control: no-store and Pragma: no-cache on the
HTTP response. Use the existing registration/token handler logic in mcp_oauth.rs
to apply these headers consistently wherever client_secret or access_token is
returned.
- Around line 1686-1688: The current is_loopback_host helper is too permissive
because host.starts_with("127.") matches non-literal hostnames that can resolve
off-loopback. Update is_loopback_host in mcp_oauth.rs to allow only exact
"localhost" (case-insensitive) and parsed IP literals whose
IpAddr::is_loopback() returns true, so the loopback redirect check cannot be
bypassed by crafted DNS names.
🪄 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: bb0acae3-b892-450d-8e0f-427a38c4bb81

📥 Commits

Reviewing files that changed from the base of the PR and between 7468487 and cc99215.

⛔ Files ignored due to path filters (1)
  • libs/cua-driver/rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • libs/cua-driver/README.md
  • libs/cua-driver/rust/crates/cua-driver/Cargo.toml
  • libs/cua-driver/rust/crates/cua-driver/src/cli.rs
  • libs/cua-driver/rust/crates/cua-driver/src/main.rs
  • libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs

Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs Outdated
Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs Outdated
Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
Comment thread libs/cua-driver/rust/crates/cua-driver/src/mcp_oauth.rs
@outdog-hwh

Copy link
Copy Markdown
Contributor Author

Follow-up for the CodeRabbit review comments, addressed in 4c95bd30:

@f-trycua

Copy link
Copy Markdown
Collaborator

We moved the architecture decision into RFC #3197 with draft document PR #3198. The proposed boundary keeps Cua Driver transport-neutral and places OAuth, DCR, TLS, tunnels, and provider-specific adapters in a separate optional gateway. This PR remains linked as the design and implementation exploration, and the RFC preserves @outdog-hwh’s credit. We should decide the RFC before continuing the in-core implementation.

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.

2 participants