Skip to main content
Executor implements OAuth 2.0 Protected Resource metadata discovery endpoints and provides anonymous authentication token issuance.

OAuth Protected Resource Metadata

Discovers OAuth configuration for MCP endpoints.
GET /.well-known/oauth-protected-resource

Query Parameters

resource
string
Resource hint URL. If provided, must match the server’s origin. Determines which MCP endpoint’s metadata to return.

Response

{
  "resource": "https://executor.example.com/v1/mcp",
  "authorization_servers": [
    "https://auth.example.com"
  ],
  "bearer_methods_supported": ["header"]
}
resource
string
The MCP endpoint URL that requires authorization
authorization_servers
string[]
Array of OAuth authorization server URLs
bearer_methods_supported
string[]
Supported bearer token transmission methods (always ["header"])

Behavior

  • If resource parameter is not provided, defaults to /v1/mcp
  • If resource points to anonymous MCP endpoints (/v1/mcp/anonymous, /mcp/anonymous), returns 404
  • If resource origin doesn’t match server origin, returns 400
  • Authorization server URL is sourced from environment:
    • MCP_AUTHORIZATION_SERVER
    • MCP_AUTHORIZATION_SERVER_URL
    • WORKOS_AUTHKIT_ISSUER
    • WORKOS_AUTHKIT_DOMAIN

Example Request

curl "https://executor.example.com/.well-known/oauth-protected-resource?resource=https://executor.example.com/v1/mcp"

Error Responses

400 Bad Request
Invalid resource hint:
{ "error": "Invalid resource hint" }
Or resource hint origin mismatch:
{ "error": "resource hint origin must match this server" }
404 Not Found
OAuth not configured (self-hosted):
{ "error": "MCP OAuth is not configured" }
Or anonymous endpoint requested:
{ "error": "Anonymous MCP does not use OAuth discovery" }
503 Service Unavailable
OAuth required but not configured (cloud):
{ "error": "MCP OAuth must be configured for cloud deployments" }

OAuth Authorization Server Metadata

Proxy endpoint for upstream authorization server metadata.
GET /.well-known/oauth-authorization-server

Response

Proxies the response from the configured authorization server’s /.well-known/oauth-authorization-server endpoint. Typical response structure:
{
  "issuer": "https://auth.example.com",
  "authorization_endpoint": "https://auth.example.com/oauth2/authorize",
  "token_endpoint": "https://auth.example.com/oauth2/token",
  "jwks_uri": "https://auth.example.com/oauth2/jwks",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"]
}

Example Request

curl https://executor.example.com/.well-known/oauth-authorization-server

Error Responses

404 Not Found
OAuth not configured (self-hosted):
{ "error": "MCP OAuth is not configured" }
503 Service Unavailable
OAuth required but not configured (cloud):
{ "error": "MCP OAuth must be configured for cloud deployments" }

Anonymous Authentication Token

Issues short-lived JWT tokens for anonymous account access.
POST /auth/anonymous/token
GET /auth/anonymous/token
Both methods are supported and behave identically.

Rate Limiting

30 requests per minute per IP + user agent.

Response

{
  "tokenType": "Bearer",
  "accessToken": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImFub255bW91cy1hdXRoIiwidHlwIjoiSldUIn0...",
  "accountId": "anon_abc123def456789",
  "expiresAtMs": 1234567890000
}
tokenType
string
Always "Bearer"
accessToken
string
ES256-signed JWT token. Contains:
  • iss - Issuer (deployment URL)
  • sub - Anonymous account ID
  • aud - Audience (executor-anonymous)
  • provider - Always "anonymous"
  • iat - Issued at timestamp
  • nbf - Not before (iat - 5 seconds)
  • exp - Expiration timestamp
accountId
string
Unique anonymous account identifier. Format: anon_<32 hex chars>
expiresAtMs
number
Token expiration time in milliseconds since Unix epoch

Token Lifetime

Default: 3600 seconds (1 hour). Configurable via ANONYMOUS_AUTH_TOKEN_TTL_SECONDS environment variable.

Example Request

curl -X POST https://executor.example.com/auth/anonymous/token
curl https://executor.example.com/auth/anonymous/token

Error Responses

400 Bad Request
Token generation failed:
{ "error": "Failed to issue anonymous token" }
429 Too Many Requests
Rate limit exceeded:
{ "error": "Rate limit exceeded" }
Includes Retry-After header (seconds).
503 Service Unavailable
Anonymous auth not configured:
{ "error": "Anonymous auth is not configured" }

Configuration Requirements

Anonymous token issuance requires:
ANONYMOUS_AUTH_PRIVATE_KEY_PEM
string
required
ES256 private key in PEM format. Used to sign tokens. Supports \n escape sequences.
ANONYMOUS_AUTH_PUBLIC_KEY_PEM
string
required
ES256 public key in PEM format. Used by JWKS endpoint. Supports \n escape sequences.
ANONYMOUS_AUTH_TOKEN_TTL_SECONDS
number
Token lifetime in seconds. Default: 3600 (1 hour).

JSON Web Key Set (JWKS)

Publishes the public key for verifying anonymous tokens.
GET /.well-known/jwks.json

Response

{
  "keys": [
    {
      "kty": "EC",
      "crv": "P-256",
      "x": "...",
      "y": "...",
      "kid": "anonymous-auth",
      "alg": "ES256",
      "use": "sig"
    }
  ]
}
keys
array
Array of JWK public keys. Contains a single ES256 key:
  • kid - Key ID (always "anonymous-auth")
  • alg - Algorithm (always "ES256")
  • use - Key usage (always "sig")
  • kty - Key type (always "EC")
  • crv - Curve (always "P-256")
  • x, y - Public key coordinates (Base64URL-encoded)

Example Request

curl https://executor.example.com/.well-known/jwks.json

Error Responses

503 Service Unavailable
Anonymous auth not configured:
{ "error": "Anonymous auth is not configured" }

Implementation Details

Source files:
  • OAuth handlers: executor/packages/database/convex/http/oauth_handlers.ts:11-86
  • Anonymous auth: executor/packages/database/convex/http/anonymous_auth.ts:91-159
  • MCP auth config: executor/packages/database/convex/http/mcp_auth.ts:10-79
  • Rate limiting: executor/packages/database/convex/http/rate_limit.ts:84-93

Token Verification

Anonymous tokens are verified using the JWKS endpoint. Token claims:
{
  "alg": "ES256",
  "kid": "anonymous-auth",
  "typ": "JWT",
  "iss": "https://executor.example.com",
  "sub": "anon_abc123def456789",
  "aud": "executor-anonymous",
  "provider": "anonymous",
  "iat": 1234567890,
  "nbf": 1234567885,
  "exp": 1234571490
}
The provider: "anonymous" claim distinguishes anonymous tokens from WorkOS tokens.

Build docs developers (and LLMs) love