Skip to content

[FIX][UI]: Fix OAuth tool fetching#3062

Merged
crivetimihai merged 2 commits intomainfrom
bugfix/oauth-cookie
Feb 20, 2026
Merged

[FIX][UI]: Fix OAuth tool fetching#3062
crivetimihai merged 2 commits intomainfrom
bugfix/oauth-cookie

Conversation

@madhav165
Copy link
Copy Markdown
Collaborator

@madhav165 madhav165 commented Feb 19, 2026

🐛 Bug-fix PR

Closes #3059


📌 Summary

Fixes a bug where clicking "Fetch Tools" button in the OAuth callback page returns a 401 error with message "Cookie authentication not allowed for API requests. Use Authorization header."

🐞 Root Cause

The RBAC middleware (mcpgateway/middleware/rbac.py) rejects cookie-only authentication for non-browser requests. The fetch request from the OAuth callback page was missing:

  • Browser identification headers (hx-request: "true", Accept: text/html)

Without these, the request was treated as an API request, triggering the 401 error.

💡 Fix Description

mcpgateway/routers/oauth_router.py

Added browser identification headers to the fetch request in the OAuth callback template:

 const response = await fetch('{safe_root_path}/oauth/fetch-tools/{escape(str(gateway_id))}', {
-    method: 'POST'
+    method: 'POST',
+    headers: {
+        'hx-request': 'true',
+        'Accept': 'text/html'
+    }
 });

🧪 Verification

Check Command Status
Lint suite make lint pass
Unit tests make test pass

📐 MCP Compliance (if relevant)

  • Matches current MCP spec
  • No breaking change to MCP clients

✅ Checklist

  • Code formatted (make black isort pre-commit)
  • No secrets/credentials committed

@madhav165 madhav165 added ui User Interface ica ICA related issues MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe labels Feb 19, 2026
@madhav165 madhav165 changed the title [FIX][UI]: Fetch Tools After OAuth Fails with 401 Error [FIX][UI]: Fix OAuth tool fetching Feb 19, 2026
Copy link
Copy Markdown
Member

@crivetimihai crivetimihai left a comment

Choose a reason for hiding this comment

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

This works around the 401, but the approach has issues:

  1. Fake hx-request: true header — this is not an HTMX request. It's a plain fetch() from inline JS. Any middleware or logging that branches on hx-request will misclassify this request.

  2. Accept: text/html is semantically wrong — the endpoint returns JSON ({"success": true, "message": "..."}), and the response handler calls response.json(). Claiming the client wants HTML back is misleading.

  3. Incomplete — doesn't fix admin.js:19594 fetchToolsForGateway() (which has the same pattern), and doesn't add credentials: "include" for cross-origin safety.

The underlying issue (as documented in #3059) is that the RBAC referer check at rbac.py:259 only matches "/admin" in referer. The OAuth success page's referer is /oauth/callback, which doesn't match.

The cleaner fix would be:

  • rbac.py:259: Broaden is_admin_ui_request to also match /oauth/ in the referer
  • oauth_router.py + admin.js: Add credentials: "include" (defensive, for cross-origin deployments)

That way no fake headers are needed, and both call sites are covered.

Copy link
Copy Markdown
Member

@crivetimihai crivetimihai left a comment

Choose a reason for hiding this comment

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

Thanks @madhav165 — this is exactly the right fix. Clean and correct.

What changed (all good):

  1. rbac.py — broadened the referer check to include /oauth/:

    is_admin_ui_request = "/admin" in referer or "/oauth/" in referer

    This correctly treats the OAuth callback page as a browser request, allowing cookie auth to pass through.

  2. oauth_router.py + admin.js — added credentials: 'include' to the fetch-tools call, so the browser actually sends the jwt_token cookie with the request.

  3. Tests — three solid cases covering admin referer (allowed), OAuth referer (allowed), and unrelated referer (rejected). Good coverage of the boundary.

The fake-header approach from the earlier revision is gone, and the fix works at the right layer (referer-based browser detection + standard cookie inclusion). LGTM.

@crivetimihai crivetimihai self-assigned this Feb 20, 2026
crivetimihai
crivetimihai previously approved these changes Feb 20, 2026
Copy link
Copy Markdown
Member

@crivetimihai crivetimihai left a comment

Choose a reason for hiding this comment

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

LGTM — clean, minimal fix with good test coverage.

Rebased onto main and squashed into a single commit.

Changes reviewed:

  • credentials: 'include' on both fetch() calls (oauth callback page + admin.js) — consistent with ~18 other admin.js fetch calls
  • /oauth/ referer detection in RBAC middleware — mirrors existing /admin pattern
  • 3 new unit tests covering admin referer, oauth referer, and unrelated referer rejection
  • 100% line coverage on mcpgateway/middleware/rbac.py

Security: No concerns — referer is browser-enforced (same-origin policy), cookies are httponly + samesite=lax, and cookie auth remains a secondary mechanism behind Bearer tokens.

Add `credentials: 'include'` to the fetch-tools request in both the
OAuth callback page and admin.js so the browser sends the jwt_token
cookie.  The OAuth callback fetch also sets `Accept: text/html` so the
RBAC middleware recognises it as a browser request through the existing
accept-header check, avoiding the need to expand referer-based trust
to `/oauth/` paths.

Closes #3059

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai
Copy link
Copy Markdown
Member

Security hardening: replaced referer-based /oauth/ check with Accept header

Rebased and squashed again with a security improvement. The original approach added "/oauth/" in referer to the RBAC browser-request detection. While not exploitable from browsers (SameSite=Lax blocks cross-origin POST cookies, CORS restricts credentialed requests, and Referer-Policy strips paths cross-origin), it unnecessarily expanded the referer trust surface at the code level.

What changed from the previous push:

  • Reverted the /oauth/ addition to the referer check in rbac.py — this line is now unchanged from main
  • Added Accept: text/html header to the OAuth callback page's fetch call — this triggers the existing "text/html" in accept_header browser-request detection, so no referer expansion is needed
  • Added regression test (test_cookie_auth_rejected_with_cross_origin_oauth_referer) confirming that a cross-origin /oauth/ referer with Accept: application/json is properly rejected with 401

Why not origin-validate the referer (ChatGPT's suggestion)?
ProxyHeadersMiddleware is configured with trusted_hosts="*", so request.url may differ from the public-facing URL behind a reverse proxy. Origin comparison between request.url and Referer would break admin UI cookie auth in proxied deployments.

Files changed (3):

File Change
oauth_router.py credentials: 'include' + Accept: text/html on callback fetch
admin.js credentials: 'include' on admin fetch (referer /admin handles detection)
test_rbac.py +4 tests: admin referer allow, accept-header allow, cross-origin reject, unrelated referer reject

All 168 tests pass, 100% coverage on rbac.py.

@crivetimihai
Copy link
Copy Markdown
Member

Acknowledged the Referrer-Policy nuance — our header governs responses from our app, not attacker-controlled pages. The browser default (strict-origin-when-cross-origin) provides equivalent path-stripping, but SameSite=Lax is the primary CSRF blocker for cross-origin POST regardless.

The pre-existing /admin referer trust is out of scope for this PR.

This is ready to merge.

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai crivetimihai merged commit ef8746d into main Feb 20, 2026
46 checks passed
@crivetimihai crivetimihai deleted the bugfix/oauth-cookie branch February 20, 2026 12:28
vishu-bh pushed a commit that referenced this pull request Feb 24, 2026
* fix(ui): fix OAuth tool fetching cookie auth

Add `credentials: 'include'` to the fetch-tools request in both the
OAuth callback page and admin.js so the browser sends the jwt_token
cookie.  The OAuth callback fetch also sets `Accept: text/html` so the
RBAC middleware recognises it as a browser request through the existing
accept-header check, avoiding the need to expand referer-based trust
to `/oauth/` paths.

Closes #3059

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* Fix pre-commit

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Madhav Kandukuri <madhav165@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ica ICA related issues MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe ui User Interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(auth): RBAC rejects OAuth success page fetch-tools as non-browser request

2 participants