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:
- Generate a random
code_verifier(43-128 characters, URL-safe) - Compute
code_challenge = BASE64URL(SHA256(code_verifier)) - Send
code_challengein the authorization request - Send
code_verifierin 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
intervalfield (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 |