Skip to main content

Overview

Secure MCP Gateway provides comprehensive OAuth 2.0 and 2.1 support for authenticating with remote MCP servers. This includes client credentials flow, mutual TLS (mTLS), automatic token management, and secure token caching.

Supported Features

OAuth 2.0 & 2.1

Full specification compliance with security best practices

Client Credentials

Server-to-server authentication flow

Mutual TLS (mTLS)

Enhanced security with client certificates (RFC 8705)

Token Caching

Automatic caching with proactive refresh

Token Revocation

RFC 7009 compliant token revocation

Scope Validation

Verifies returned tokens have requested scopes

Retry Logic

Exponential backoff for transient failures

Metrics & Monitoring

Comprehensive tracking of OAuth operations

Quick Start

Basic OAuth 2.1 Setup

1

Obtain OAuth Credentials

Get your client ID and secret from your OAuth provider (Auth0, Okta, Keycloak, etc.)
2

Add Server with OAuth

secure-mcp-gateway config add-server \
  --config-name "default_config" \
  --server-name "oauth-api" \
  --server-command "npx" \
  --args="-y,mcp-remote,https://api.example.com/mcp" \
  --description "OAuth-protected remote server"
3

Configure OAuth

Edit ~/.enkrypt/enkrypt_mcp_config.json and add OAuth config:
{
  "server_name": "oauth-api",
  "oauth_config": {
    "enabled": true,
    "is_remote": true,
    "OAUTH_VERSION": "2.1",
    "OAUTH_CLIENT_ID": "your-client-id",
    "OAUTH_CLIENT_SECRET": "your-client-secret",
    "OAUTH_TOKEN_URL": "https://auth.example.com/oauth/token",
    "OAUTH_AUDIENCE": "https://api.example.com"
  }
}
4

Test Connection

Restart Claude Desktop and test:
List all servers and discover tools from oauth-api

Configuration Reference

Minimal Configuration

{
  "oauth_config": {
    "enabled": true,
    "OAUTH_VERSION": "2.1",
    "OAUTH_GRANT_TYPE": "client_credentials",
    "OAUTH_CLIENT_ID": "your-client-id",
    "OAUTH_CLIENT_SECRET": "your-client-secret",
    "OAUTH_TOKEN_URL": "https://auth.example.com/oauth/token"
  }
}

Complete Configuration

{
  "oauth_config": {
    // Required
    "enabled": true,
    "OAUTH_VERSION": "2.1",
    "OAUTH_GRANT_TYPE": "client_credentials",
    "OAUTH_CLIENT_ID": "your-client-id",
    "OAUTH_CLIENT_SECRET": "your-client-secret",
    "OAUTH_TOKEN_URL": "https://auth.example.com/oauth/token",
    
    // Server detection
    "is_remote": true,
    
    // Token scope and audience
    "OAUTH_AUDIENCE": "https://api.example.com",
    "OAUTH_ORGANIZATION": "org-123",
    "OAUTH_SCOPE": "read write delete",
    "OAUTH_RESOURCE": "https://resource.example.com",
    
    // Security settings
    "OAUTH_USE_BASIC_AUTH": true,
    "OAUTH_ENFORCE_HTTPS": true,
    "OAUTH_TOKEN_IN_HEADER_ONLY": true,
    "OAUTH_VALIDATE_SCOPES": true,
    
    // Token management
    "OAUTH_TOKEN_EXPIRY_BUFFER": 300,
    
    // Mutual TLS (optional)
    "OAUTH_USE_MTLS": false,
    "OAUTH_CLIENT_CERT_PATH": "~/.certs/client.pem",
    "OAUTH_CLIENT_KEY_PATH": "~/.certs/client-key.pem",
    "OAUTH_CA_BUNDLE_PATH": "~/.certs/ca-bundle.pem",
    
    // Token revocation
    "OAUTH_REVOCATION_URL": "https://auth.example.com/oauth/revoke",
    
    // Advanced
    "OAUTH_ADDITIONAL_PARAMS": {},
    "OAUTH_CUSTOM_HEADERS": {}
  }
}

Configuration Fields

Core Settings

FieldTypeRequiredDefaultDescription
enabledbooleanYesfalseEnable OAuth for this server
OAUTH_VERSIONstringNo"2.0"OAuth version: "2.0" or "2.1"
OAUTH_GRANT_TYPEstringNo"client_credentials"Only client_credentials supported
OAUTH_CLIENT_IDstringYes*-OAuth client ID
OAUTH_CLIENT_SECRETstringYes*-OAuth client secret
OAUTH_TOKEN_URLstringYes-Token endpoint (HTTPS required for 2.1)
*Required when enabled: true

Server Detection

FieldTypeDescription
is_remotebooleanExplicitly mark as remote server (recommended)
The gateway auto-detects remote servers by checking for npx, mcp-remote, curl, or http(s):// in command/args. Set is_remote: true explicitly to avoid false detection.

Scope & Audience

FieldTypeDescription
OAUTH_AUDIENCEstringIntended audience for the token
OAUTH_ORGANIZATIONstringOrganization identifier
OAUTH_SCOPEstringSpace-separated scopes (e.g., "read write")
OAUTH_RESOURCEstringResource Indicator (OAuth 2.1, RFC 8707)

Security Settings

FieldTypeDefaultDescription
OAUTH_USE_BASIC_AUTHbooleantrueUse HTTP Basic Auth (client_secret_basic)
OAUTH_ENFORCE_HTTPSbooleantrueRequire HTTPS for token URL
OAUTH_TOKEN_IN_HEADER_ONLYbooleantrueNever use query params for tokens
OAUTH_VALIDATE_SCOPESbooleantrueValidate returned token has requested scopes
OAuth 2.1 Compliance
  • Requires OAUTH_ENFORCE_HTTPS: true
  • Recommends OAUTH_USE_BASIC_AUTH: true
  • Enforces OAUTH_TOKEN_IN_HEADER_ONLY: true

Token Management

FieldTypeDefaultDescription
OAUTH_TOKEN_EXPIRY_BUFFERinteger300Seconds before expiry to refresh token
How Token Refresh Works:
1

Token Acquired

Token obtained with expires_in: 3600 (1 hour)
2

Cached with Expiry

Cached with expires_at timestamp
3

Proactive Refresh

Refreshed when expires_at - 300s < now (5 min before expiry)
4

Automatic Retry

On failure, retry with exponential backoff (2s, 4s, 8s)

Mutual TLS (mTLS)

What is mTLS?

Mutual TLS provides enhanced security by requiring both client and server to present certificates during the TLS handshake. This is specified in RFC 8705.

Setting Up mTLS

1

Generate Client Certificate

Generate a client certificate and private key:
# Generate private key
openssl genrsa -out client-key.pem 2048

# Generate certificate signing request
openssl req -new -key client-key.pem -out client.csr

# Generate self-signed certificate (or get signed by CA)
openssl x509 -req -days 365 -in client.csr \
  -signkey client-key.pem -out client.pem

# Set permissions
chmod 600 client-key.pem client.pem
2

Upload Certificate to OAuth Provider

Register your client certificate with your OAuth provider (Auth0, Okta, etc.)
3

Configure Gateway

{
  "oauth_config": {
    "enabled": true,
    "OAUTH_USE_MTLS": true,
    "OAUTH_CLIENT_CERT_PATH": "~/.certs/client.pem",
    "OAUTH_CLIENT_KEY_PATH": "~/.certs/client-key.pem",
    "OAUTH_CA_BUNDLE_PATH": "~/.certs/ca-bundle.pem",
    "OAUTH_CLIENT_ID": "your-client-id",
    "OAUTH_CLIENT_SECRET": "your-client-secret",
    "OAUTH_TOKEN_URL": "https://auth.example.com/oauth/token"
  }
}
4

Test Connection

The gateway will automatically use the certificate for all OAuth requests.
Certificate Security
  • Store certificates in secure directory with chmod 600
  • Never commit certificates to version control
  • Rotate certificates before expiry
  • Use different certificates for different environments

Token Injection

For Remote Servers

Tokens are automatically injected as HTTP headers:
npx -y mcp-remote https://api.example.com/mcp \
  --header "Authorization: Bearer eyJhbGci..."
The gateway handles this automatically when is_remote: true.

For Local Servers

Tokens are injected as environment variables:
ENKRYPT_ACCESS_TOKEN=eyJhbGci...
AUTHORIZATION=Bearer eyJhbGci...
OAUTH_ACCESS_TOKEN=eyJhbGci...
OAUTH_TOKEN_TYPE=Bearer
HTTP_HEADER_Authorization=Bearer eyJhbGci...
HTTP_HEADER_AUTHORIZATION=Bearer eyJhbGci...
Your MCP server can read these variables to authenticate with remote APIs.

Provider-Specific Examples

Auth0

{
  "oauth_config": {
    "enabled": true,
    "OAUTH_VERSION": "2.1",
    "OAUTH_GRANT_TYPE": "client_credentials",
    "OAUTH_CLIENT_ID": "your-auth0-client-id",
    "OAUTH_CLIENT_SECRET": "your-auth0-client-secret",
    "OAUTH_TOKEN_URL": "https://yourtenant.auth0.com/oauth/token",
    "OAUTH_AUDIENCE": "https://yourapi.example.com",
    "OAUTH_SCOPE": "read:data write:data"
  }
}

Okta

{
  "oauth_config": {
    "enabled": true,
    "OAUTH_VERSION": "2.0",
    "OAUTH_CLIENT_ID": "okta-client-id",
    "OAUTH_CLIENT_SECRET": "okta-client-secret",
    "OAUTH_TOKEN_URL": "https://dev-123456.okta.com/oauth2/default/v1/token",
    "OAUTH_REVOCATION_URL": "https://dev-123456.okta.com/oauth2/default/v1/revoke",
    "OAUTH_AUDIENCE": "api://default",
    "OAUTH_SCOPE": "custom.scope"
  }
}

Keycloak

{
  "oauth_config": {
    "enabled": true,
    "OAUTH_VERSION": "2.0",
    "OAUTH_CLIENT_ID": "keycloak-client",
    "OAUTH_CLIENT_SECRET": "keycloak-secret",
    "OAUTH_TOKEN_URL": "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token",
    "OAUTH_AUDIENCE": "account",
    "OAUTH_SCOPE": "openid profile email",
    "OAUTH_ADDITIONAL_PARAMS": {
      "realm": "myrealm"
    }
  }
}

GitHub Apps (mTLS)

{
  "oauth_config": {
    "enabled": true,
    "OAUTH_VERSION": "2.0",
    "OAUTH_CLIENT_ID": "github-app-client-id",
    "OAUTH_CLIENT_SECRET": "github-app-client-secret",
    "OAUTH_TOKEN_URL": "https://github.com/login/oauth/access_token",
    "OAUTH_USE_MTLS": true,
    "OAUTH_CLIENT_CERT_PATH": "~/.ssh/github-app.pem",
    "OAUTH_CLIENT_KEY_PATH": "~/.ssh/github-app-key.pem"
  }
}

Monitoring & Troubleshooting

Enable Debug Logging

{
  "common_mcp_gateway_config": {
    "enkrypt_log_level": "DEBUG"
  }
}
Logs will show:
[OAuthService] Token request correlation_id=f47ac10b for oauth-api
[OAuthService] Successfully obtained token, expires in 3600s
[OAuthService] Token cached for oauth-api (config: abc-123)

Common Errors

OAuth token request failed: invalid_client
Solution: Verify OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET are correct
OAuth 2.1 requires HTTPS for token_url
Solution: Use https:// URL or set OAUTH_VERSION: "2.0"
Client certificate not found: /path/to/cert.pem
Solution: Verify certificate path and file permissions (chmod 600)
Token scopes validation failed. Requested: read write, Received: read
Solution: Request correct scopes or set OAUTH_VALIDATE_SCOPES: false
Retrying token acquisition for oauth-api, attempt 2/3
Info: Automatic retry with exponential backoff (2s, 4s, 8s)

Viewing Metrics

OAuth metrics are available programmatically:
from secure_mcp_gateway.services.oauth import get_oauth_service

oauth_service = get_oauth_service()
metrics = oauth_service.get_metrics()

print(metrics)
# {
#   "token_acquisitions_total": 150,
#   "token_acquisitions_success": 148,
#   "token_acquisitions_failure": 2,
#   "token_cache_hits": 1200,
#   "token_cache_misses": 150,
#   "token_refreshes": 45,
#   "cache_hit_ratio": 0.889,
#   "success_rate": 0.987,
#   "avg_latency_ms": 234.5,
#   "active_tokens": 12
# }

Security Best Practices

Use OAuth 2.1

OAuth 2.1 has stricter security requirements and removes deprecated features

Enable HTTPS

Always use HTTPS for token URLs in production

Use Basic Auth

Prefer client_secret_basic over client_secret_post

Validate Scopes

Always verify returned tokens have requested scopes

Enable mTLS

Use mutual TLS for high-security environments

Rotate Secrets

Regularly rotate client secrets and certificates

Minimal Scopes

Request only the scopes you need (principle of least privilege)

Secure Storage

Store certificates with proper permissions (chmod 600)

Security Checklist

HTTPS Enforced

OAUTH_ENFORCE_HTTPS: true for production

Scope Validation Enabled

OAUTH_VALIDATE_SCOPES: true

Credentials Not Committed

Add .enkrypt/ to .gitignore

Certificates Secured

chmod 600 on all certificate files

Different Creds per Environment

Separate client IDs for dev/staging/prod

Monitoring Enabled

Track OAuth metrics and failures

Advanced Topics

Token Revocation

Revoke tokens programmatically:
from secure_mcp_gateway.services.oauth import get_oauth_service

oauth_service = get_oauth_service()
success, error = await oauth_service.revoke_token(
    server_name="oauth-api",
    token="access_token_to_revoke",
    oauth_config=config,
    token_type_hint="access_token"
)

Force Token Refresh

from secure_mcp_gateway.services.oauth import refresh_server_oauth_token

token, error = await refresh_server_oauth_token(
    server_name="oauth-api",
    server_entry=server_config,
    config_id="config-id"
)

Invalidate Cached Token

from secure_mcp_gateway.services.oauth import invalidate_server_oauth_token

await invalidate_server_oauth_token("oauth-api", "config-id")

FAQ

Not yet. Only client credentials grant is currently supported. Authorization code grant will be added in a future release.
Set is_remote: true explicitly in oauth_config. Auto-detection checks for npx, mcp-remote, curl, or http(s):// in command/args.
Yes! Tokens are injected as environment variables for stdio servers.
Proactively, 5 minutes (300s) before expiry by default. Configurable via OAUTH_TOKEN_EXPIRY_BUFFER.
Yes. mTLS is optional and disabled by default.
Automatic retry with exponential backoff (3 attempts: 2s, 4s, 8s). After exhaustion, returns error.
No. Each server has its own cached token, keyed by server_name and config_id.

Next Steps

Add MCP Servers

Learn how to add and configure MCP servers

Configure Guardrails

Add security layers to OAuth-protected servers

External Cache

Improve token caching with Redis/KeyDB

Custom Plugins

Create custom auth providers

Build docs developers (and LLMs) love