Skip to content

[BUG][AUTH]: OAuth resource field missing from UI causes audience validation failures with real OAuth providers #4384

@MohanLaksh

Description

@MohanLaksh

Summary

The OAuth token audience validation added in PR #3941 fails for real-world OAuth providers (Salesforce, Azure AD, etc.) because:

  1. The resource field in oauth_config is not exposed in the Admin UI
  2. The fallback behavior uses the full gateway.url (including path) as the expected audience
  3. Most OAuth providers issue tokens with audiences at the origin level, not the full endpoint path

This causes legitimate OAuth flows to fail with "Token audience mismatch" errors that users cannot fix via the UI.

Severity: HIGH - Regression bug that breaks OAuth functionality for common enterprise IdPs


Root Cause Analysis

Timeline

  1. March 20, 2026: Documentation (oauth-design.md:131) explicitly documented as "Known Gap" that audience/scope validation did not exist
  2. April 6, 2026: PR Add OAuth token claim validation before MCP server forwarding #3941 added token validation to fix security bug [BUG][AUTH]: OAuth OBO flow missing token audience and scope validation before MCP server call #3563
  3. Current state: Validation exists but has implementation flaws that break real OAuth flows

Cause → Effect Chain

CAUSE 1: PR #3941 added audience validation
    ↓
EFFECT 1: Code now validates token aud claim against expected audience
    ↓
CAUSE 2: Expected audience falls back to gateway.url when resource not configured
    ↓  (token_validation_service.py:155)
    |  expected_audience = oauth_config.get("resource") or gateway_url
    ↓
CAUSE 3: resource field is NOT exposed in Admin UI
    ↓  (admin.html:5689-5967 - OAuth form has no oauth_resource field)
    ↓
EFFECT 2: All OAuth configurations use fallback behavior
    ↓
CAUSE 4: gateway.url contains full endpoint path
    ↓  Example: "https://api.salesforce.com/platform/mcp/v1/platform/sobject-all"
    ↓
EFFECT 3: Expected audience is overly specific
    ↓
CAUSE 5: Real OAuth providers issue tokens with origin-level audiences
    ↓  Example token aud: "https://api.salesforce.com"
    ↓
EFFECT 4: Audience mismatch → blocking error
    ↓  (gateway_service.py:1598-1601)
    |  blocking = token_validation.blocking_errors
    |  if blocking:
    |      raise GatewayConnectionError(...)
    ↓
FINAL EFFECT: OAuth authorization succeeds but tool fetch fails
    ✗ Users see: "Refusing to forward OAuth token... Token audience mismatch"
    ✗ Error message says to fix oauth_config but UI doesn't expose the field
    ✗ Users must resort to API calls or direct DB updates

Affected Code Locations

1. Token Validation (The Check)

File: mcpgateway/services/token_validation_service.py:155

expected_audience = oauth_config.get("resource") or gateway_url
  • ✅ Correctly checks for resource field
  • ❌ Fallback to gateway_url uses full path instead of origin

2. Gateway Service (The Block)

File: mcpgateway/services/gateway_service.py:1586-1601

token_validation = validate_oauth_token_claims(...)
blocking = token_validation.blocking_errors
if blocking:
    raise GatewayConnectionError(f"Refusing to forward OAuth token...")
  • ✅ Validation happens before forwarding token
  • ❌ Blocks legitimate tokens due to overly specific audience expectation

3. Admin UI Form (The Gap)

File: mcpgateway/templates/admin.html:5689-5967

  • OAuth form fields exposed: grant_type, issuer, client_id, client_secret, username, password, token_url, authorization_url, scopes
  • MISSING: oauth_resource field

4. Backend Form Handler (The Gap)

File: mcpgateway/admin.py:12070-12090

  • Form processing reads: oauth_grant_type, oauth_issuer, oauth_client_id, etc.
  • MISSING: oauth_resource extraction from form

5. URL Normalization (Insufficient)

File: mcpgateway/routers/oauth_router.py:_normalize_resource_url()

normalized = urlunparse((parsed.scheme, parsed.netloc, parsed.path, ...))
  • Strips query/fragment but preserves path
  • ❌ Does not extract origin when used as fallback

Steps to Reproduce

Prerequisites

  • ContextForge instance with Admin UI enabled
  • OAuth provider (Salesforce, Azure AD, Keycloak, etc.)

Reproduction Steps

  1. Register an MCP server gateway with OAuth via Admin UI:

    • Name: "Salesforce Hosted v2"
    • URL: https://api.salesforce.com/platform/mcp/v1/platform/sobject-all
    • Auth Type: OAuth 2.0
    • Configure OAuth fields:
      • Grant Type: Authorization Code
      • Issuer: https://login.salesforce.com
      • Authorization URL: https://login.salesforce.com/services/oauth2/authorize
      • Token URL: https://login.salesforce.com/services/oauth2/token
      • Client ID: <your-client-id>
      • Client Secret: <your-client-secret>
      • Scopes: api refresh_token
      • Note: No "OAuth Resource" field exists in UI
  2. Complete OAuth authorization:

    • Click "Authorize" on the gateway
    • Authenticate with Salesforce
    • OAuth flow succeeds, tokens stored
  3. Attempt to fetch tools:

    • Click "Fetch Tools"
    • Observe: Request fails with error

Actual Behavior

Error message:

❌ Failed to Fetch Tools
Error: Failed to fetch tools: Failed to fetch tools after OAuth: Refusing to forward OAuth token for gateway 'Salesforce Hosted v2': Token audience mismatch: token aud=[https://orchestrate-agentforce-dev-ed.develop.my.salesforce.com, https://api.salesforce.com], expected 'https://api.salesforce.com/platform/mcp/v1/platform/sobject-all'. Fix oauth_config (resource/scopes/issuer) or the IdP token request.

Why it fails:

  • Token audience: ["https://orchestrate-agentforce-dev-ed.develop.my.salesforce.com", "https://api.salesforce.com"]
  • Expected audience: "https://api.salesforce.com/platform/mcp/v1/platform/sobject-all" (full gateway URL)
  • No match → blocking error

User cannot fix: Error says to "fix oauth_config (resource/...)" but UI doesn't expose this field

Expected Behavior

  1. Option A: Admin UI should expose "OAuth Resource" field

    • Users can explicitly configure: https://api.salesforce.com
    • Validation would succeed: https://api.salesforce.com ∈ token aud list
  2. Option B: Fallback should extract origin from gateway URL

    • Auto-derived resource: https://api.salesforce.com (not the full path)
    • Validation would succeed without manual configuration
  3. Option C: Validation should be advisory, not blocking

    • Log warnings but allow forwarding token to MCP server
    • Let upstream server be authoritative (original design per docs)

Impact Assessment

Affected Users

  • Works: Test environments where token aud accidentally matches full gateway URL
  • Broken: Production OAuth with Salesforce, Azure AD, Google, Okta, Keycloak
  • Broken: Any OAuth provider issuing origin-level audiences (industry standard)

Workarounds

Workaround 1: API Update (requires technical expertise)

curl -X PATCH "http://localhost:4444/admin/gateways/{GATEWAY_ID}" \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"oauth_config": {..., "resource": "https://api.salesforce.com"}}'

Workaround 2: Direct DB Update (requires DB access)

UPDATE gateways 
SET oauth_config = json_set(oauth_config, '$.resource', 'https://api.salesforce.com')
WHERE name = 'Salesforce Hosted v2';

Problems with workarounds:

  • Require technical expertise beyond typical admin users
  • Not discoverable (error message doesn't mention API/DB workaround)
  • Poor UX - error says "fix oauth_config" but UI doesn't allow it

Proposed Solution

Option 1: Add OAuth Resource Field to UI (Recommended)

Changes needed:

  1. Frontend: mcpgateway/templates/admin.html (3 locations)

    • Add new form field after "Scopes" field
  2. Backend: mcpgateway/admin.py:12070-12090

    • Extract oauth_resource from form
  3. Validation Fallback: mcpgateway/services/token_validation_service.py:155

    • Change fallback to extract origin instead of using full path

Pros:

  • ✅ Users can explicitly configure resource
  • ✅ Fallback behavior improved for auto-configuration
  • ✅ Backward compatible (existing configs keep working)
  • ✅ Aligns with OAuth best practices (RFC 8707)

Option 2: Hybrid Approach (Best Balance)

Combine explicit configuration + improved fallback + smart blocking:

  1. Add UI field for explicit configuration
  2. Improve fallback to use origin extraction
  3. Soften validation: Only block if resource was explicitly configured and mismatched

Pros:

  • ✅ Best UX - explicit configs get fail-fast, auto-configs are permissive
  • ✅ Maintains security for intentional configurations
  • ✅ Doesn't break auto-configurations with imperfect fallback
  • ✅ Provides diagnostic value without being draconian

Testing Requirements

Before Merge: Required Tests

1. Unit Tests (tests/unit/mcpgateway/services/test_token_validation_service.py)

  • Test: Audience validation with explicit resource (origin level)
  • Test: Audience validation with gateway URL fallback (extracts origin)
  • Test: Audience mismatch with explicit resource is blocking
  • Test: Audience array contains expected resource

2. Integration Tests (tests/e2e/test_oauth_audience_validation.py)

  • Test: OAuth flow with origin-level audience succeeds
  • Test: OAuth flow with explicit resource configuration
  • Test: OAuth flow with audience mismatch (explicit resource)

3. Admin UI Tests (tests/playwright/test_oauth_ui.py)

  • Test: OAuth Resource field visible in gateway create form
  • Test: OAuth Resource field visible in gateway edit form
  • Test: OAuth Resource field persists on save
  • Test: OAuth Resource field validation

4. Regression Tests

  • Test: Existing gateways without resource field still work
  • Test: Legacy test still passes (if updated)

5. Manual Testing Checklist

  • Salesforce OAuth
  • Azure AD OAuth
  • Google OAuth
  • UI Field Visibility
  • Error Messages

Documentation Updates Required

1. Update docs/docs/architecture/oauth-design.md

  • Remove outdated "Known Gap" section (line 131)
  • Add new "Token Claim Validation" section

2. Create docs/docs/manage/oauth-resource-configuration.md

  • New guide explaining OAuth resource parameter (RFC 8707)
  • When to configure explicitly vs auto-derivation
  • Common patterns for different providers

3. Update docs/docs/manage/oauth-troubleshooting.md

  • Add "Token audience mismatch" error section with diagnosis and fixes

Related Issues & PRs


Acceptance Criteria

Functional

  • OAuth Resource field is visible in Admin UI (create/edit gateway forms)
  • OAuth Resource field is optional (auto-derives origin if not set)
  • Explicitly configured resource is validated and blocks on mismatch
  • Auto-derived resource logs warnings but doesn't block
  • Existing gateways without resource field continue working
  • Salesforce OAuth flow succeeds without manual resource configuration

Code Quality

  • All unit tests pass (including new audience validation tests)
  • All integration tests pass (including new E2E OAuth tests)
  • Playwright UI tests verify field visibility and persistence
  • Code coverage for new validation logic ≥ 95%
  • No security regressions (token validation still enforced)

Documentation

  • oauth-design.md updated (remove outdated "Known Gap")
  • oauth-troubleshooting.md includes audience mismatch section
  • New guide: oauth-resource-configuration.md

UX

  • Field has clear label and help text
  • Validation errors are actionable
  • Error message distinguishes explicit vs auto-derived resource failures
  • No breaking changes for existing users

References

Metadata

Metadata

Labels

bugSomething isn't workingregressionHigh priority regressionuiUser Interface

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions