Documentation

OAuth Integration

Promptmark implements standard OAuth 2.0 protocols for MCP client authentication, following RFC 9728 (Protected Resource Metadata) and RFC 8414 (Authorization Server Metadata).

Discovery

MCP clients discover Promptmark’s auth configuration via well-known endpoints:

Protected Resource Metadata (RFC 9728)

GET /.well-known/oauth-protected-resource

Returns metadata about the protected resource (the MCP server):

{
  "resource": "https://your-instance.com/mcp",
  "authorization_servers": ["https://your-instance.com"],
  "scopes_supported": ["mcp"]
}

Authorization Server Metadata (RFC 8414)

GET /.well-known/oauth-authorization-server

Returns the authorization server configuration:

{
  "issuer": "https://your-instance.com",
  "authorization_endpoint": "https://your-instance.com/oauth/authorize",
  "token_endpoint": "https://your-instance.com/oauth/token",
  "registration_endpoint": "https://your-instance.com/oauth/register",
  "device_authorization_endpoint": "https://your-instance.com/api/oauth/device/code",
  "response_types_supported": ["code"],
  "grant_types_supported": [
    "authorization_code",
    "urn:ietf:params:oauth:grant-type:device_code"
  ],
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["mcp"]
}

Authorization Code Flow (with PKCE)

For MCP clients that can open a browser:

1. Dynamic Client Registration (Optional)

POST /oauth/register
Content-Type: application/json

{
  "client_name": "My MCP Client",
  "redirect_uris": ["http://localhost:8888/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "scope": "mcp"
}

Response:

{
  "client_id": "generated-client-id",
  "client_name": "My MCP Client",
  "redirect_uris": ["http://localhost:8888/callback"]
}

2. Authorization Request

Generate a PKCE challenge and redirect the user:

GET /oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=http://localhost:8888/callback&
  scope=mcp&
  state=random-state-value&
  code_challenge=BASE64URL_SHA256_CHALLENGE&
  code_challenge_method=S256

The user sees a consent screen and approves the request.

3. Token Exchange

After the user approves, exchange the authorization code:

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=http://localhost:8888/callback&
client_id=YOUR_CLIENT_ID&
code_verifier=ORIGINAL_CODE_VERIFIER

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 86400
}

Device Authorization Flow

For headless MCP clients (CLI tools, embedded systems):

1. Request Device Code

POST /api/oauth/device/code
Content-Type: application/json

{
  "client_id": "your-client-id",
  "scope": "mcp"
}

Response:

{
  "device_code": "abc123def456",
  "user_code": "ABCD-1234",
  "verification_uri": "https://your-instance.com/auth/device",
  "expires_in": 900,
  "interval": 5
}

2. Display Instructions

Show the user:

Visit: https://your-instance.com/auth/device
Enter code: ABCD-1234

3. Poll for Token

Poll at the specified interval:

POST /api/oauth/device/token
Content-Type: application/json

{
  "device_code": "abc123def456",
  "client_id": "your-client-id",
  "grant_type": "urn:ietf:params:oauth:grant-type:device_code"
}

While waiting: {"error": "authorization_pending"} After approval:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 86400
}

PKCE (Proof Key for Code Exchange)

PKCE is required for the authorization code flow. Promptmark supports the S256 method:

  1. Generate a random code_verifier (43-128 characters, URL-safe)
  2. Compute code_challenge = BASE64URL(SHA256(code_verifier))
  3. Send code_challenge in the authorization request
  4. Send code_verifier in the token exchange

Token Format

Access tokens are JWTs containing:

Claim Description
sub User ID
exp Expiration time (24 hours from issuance)
iat Issued at time

Rate Limits

OAuth endpoints are rate-limited to prevent abuse:

  • Device code requests: Limited per IP
  • Token polling: Respect the interval field (typically 5 seconds)
  • Authorization requests: Limited per client

Error Responses

OAuth errors follow RFC 6749:

{
  "error": "invalid_grant",
  "error_description": "Authorization code has expired"
}

Common errors:

Error Description
authorization_pending User hasn’t approved the device code yet
slow_down Polling too frequently
expired_token Device code or auth code has expired
invalid_grant Invalid or expired authorization code
invalid_client Unknown client ID