Skip to main content
External OAuth enables using your existing identity provider (Okta, Auth0, Azure AD, Cognito) instead of Portkey’s built-in authentication. Users authenticate with corporate credentials—no Portkey accounts needed.

When to Use

Organizations with existing IdPs can use External OAuth. Users authenticate through the IdP for other services, and MCP access works the same way. Common scenarios:
  • SSO for internal developers. Engineers use corporate credentials to access MCP servers
  • B2B applications. Your customers authenticate through their own IdP
  • Compliance requirements. Mandate use of your own identity infrastructure
  • No Portkey accounts. Users who aren’t in Portkey need MCP access

How It Works

1. User authenticates with your IdP
2. IdP issues a JWT (or opaque token)
3. User includes token in MCP requests to Portkey
4. Portkey validates token against your IdP
5. Request proceeds with user identity attached
Portkey never handles user credentials. Your IdP remains the source of truth for identity.

Configuration

Configure JWT validation for your MCP server. Portkey validates incoming tokens using your IdP’s public keys or introspection endpoint. The most common setup. Portkey fetches public keys from your IdP’s JWKS endpoint.
{
  "jwt_validation": {
    "jwksUri": "https://your-idp.com/.well-known/jwks.json",
    "algorithms": ["RS256"],
    "requiredClaims": ["sub", "email"]
  }
}
How it works:
  • Portkey fetches and caches your IdP’s public keys
  • Keys are cached for 24 hours by default
  • If a key rotates, Portkey automatically refetches
  • Validation happens locally (no network call per request)

Option 2: Token Introspection

For opaque tokens requiring real-time validation, or for immediate revocation.
{
  "jwt_validation": {
    "introspectEndpoint": "https://your-idp.com/oauth/introspect",
    "introspectCacheMaxAge": 300
  }
}
How it works:
  • Portkey calls your IdP’s introspection endpoint
  • Response is cached for the configured duration (default: no caching)
  • With introspectCacheMaxAge: 300, results are cached for 5 minutes
Caching tradeoff:
  • No cache: Every request calls introspection endpoint (slower, but immediate revocation)
  • With cache: Better performance, but revocation takes up to cache TTL to take effect
See JWT Validation for full configuration options.

Client Configuration

Agents include the IdP token instead of a Portkey API key:
{
  "mcpServers": {
    "linear": {
      "url": "https://mcp.portkey.ai/linear/mcp",
      "headers": {
        "Authorization": "Bearer eyJhbGciOiJSUzI1NiIs..."
      }
    }
  }
}
Or in code:
# Get token from your IdP (example using OIDC)
token = get_idp_token()  # Your IdP client

headers = {
    "Authorization": f"Bearer {token}",
}

async with streamablehttp_client(
    "https://mcp.portkey.ai/linear/mcp",
    headers=headers
) as (read, write, _):
    # MCP operations...

Validating Claim Values

Validate that tokens are issued by the correct IdP and intended for MCP access:
{
  "jwt_validation": {
    "jwksUri": "https://your-idp.com/.well-known/jwks.json",
    "algorithms": ["RS256"],
    "requiredClaims": ["sub", "email"],
    "claimValues": {
      "iss": {
        "values": "https://your-idp.com",
        "matchType": "exact"
      },
      "aud": {
        "values": ["api://mcp", "https://mcp.yourcompany.com"],
        "matchType": "contains"
      }
    }
  }
}
This prevents:
  • Tokens from other IdPs being accepted
  • Tokens intended for other services being used for MCP

Combining with Identity Forwarding

External OAuth pairs naturally with identity forwarding. Portkey validates the incoming JWT, extracts user claims, and forwards them to MCP servers.
{
  "jwt_validation": {
    "jwksUri": "https://your-idp.com/.well-known/jwks.json",
    "requiredClaims": ["sub", "email", "groups"]
  },
  "user_identity_forwarding": {
    "method": "claims_header",
    "include_claims": ["sub", "email", "groups"]
  }
}
The MCP server receives user identity without handling OAuth itself. It can use this for:
  • Authorization: Check if user belongs to required groups
  • Logging: Audit trail with user identity
  • Personalization: Customize responses based on user
See Identity Forwarding.

IdP-Specific Examples

Okta

  1. Create an authorization server in Okta Admin Console
  2. Create an OAuth application (Web or SPA)
  3. Note your issuer URL (e.g., https://dev-12345.okta.com/oauth2/default)
{
  "jwt_validation": {
    "jwksUri": "https://dev-12345.okta.com/oauth2/default/v1/keys",
    "algorithms": ["RS256"],
    "claimValues": {
      "iss": {
        "values": "https://dev-12345.okta.com/oauth2/default",
        "matchType": "exact"
      }
    }
  }
}

Auth0

  1. Create an API in Auth0 Dashboard
  2. Note your tenant domain and API identifier
{
  "jwt_validation": {
    "jwksUri": "https://your-tenant.auth0.com/.well-known/jwks.json",
    "algorithms": ["RS256"],
    "claimValues": {
      "iss": {
        "values": "https://your-tenant.auth0.com/",
        "matchType": "exact"
      },
      "aud": {
        "values": "https://your-api-identifier",
        "matchType": "exact"
      }
    }
  }
}

Azure AD / Entra ID

  1. Register an application in Azure Portal
  2. Note your tenant ID and client ID
{
  "jwt_validation": {
    "jwksUri": "https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys",
    "algorithms": ["RS256"],
    "claimValues": {
      "iss": {
        "values": "https://login.microsoftonline.com/{tenant-id}/v2.0",
        "matchType": "exact"
      },
      "aud": {
        "values": "{client-id}",
        "matchType": "exact"
      }
    }
  }
}

AWS Cognito

  1. Create a User Pool in AWS Console
  2. Note your region and pool ID
{
  "jwt_validation": {
    "jwksUri": "https://cognito-idp.{region}.amazonaws.com/{pool-id}/.well-known/jwks.json",
    "algorithms": ["RS256"],
    "claimValues": {
      "iss": {
        "values": "https://cognito-idp.{region}.amazonaws.com/{pool-id}",
        "matchType": "exact"
      }
    }
  }
}

Securing Your Setup

Validate Issuer and Audience

Always configure claimValues to verify iss and aud:
{
  "claimValues": {
    "iss": {
      "values": "https://your-idp.com",
      "matchType": "exact"
    },
    "aud": {
      "values": "your-client-id-or-api-identifier",
      "matchType": "exact"
    }
  }
}
Without this, tokens intended for other applications might be accepted.

Require Essential Claims

Require essential claims:
{
  "requiredClaims": ["sub", "email"]
}
If a claim is missing, the request is rejected.

Use Short Token Lifetimes

Configure the IdP to issue short-lived access tokens (15-60 minutes). This limits the window if a token is compromised.

Troubleshooting

”Invalid issuer” Error

The token’s iss claim doesn’t match your configuration. Verify the issuer URL exactly matches—including trailing slashes.

”Missing required claims” Error

The token doesn’t include claims you specified in requiredClaims. Check your IdP’s token configuration and scopes.

”Invalid audience” Error

The token’s aud claim doesn’t match your configuration. Verify the correct API identifier or client ID.

”JWKS fetch failed” Error

Portkey couldn’t fetch keys from your JWKS URI. Verify the URL is accessible and returns valid JWKS JSON.
TopicDescription
JWT ValidationFull configuration reference for JWT validation
Identity ForwardingPass validated claims to MCP servers
Authentication OverviewUnderstanding gateway authentication