MCP authentication¶
The MCP server requires OAuth 2.1 with PKCE — no API keys, no static tokens. It reuses your existing DK SaaS portal account; there’s no separate signup for MCP access.
Discovery¶
Standard OAuth/MCP discovery documents are published at:
/.well-known/oauth-authorization-server/.well-known/oauth-protected-resource/.well-known/jwks.json/.well-known/openid-configuration
Issuer and protected resource are both under dishonkadoh.com:
{
"issuer": "https://dishonkadoh.com",
"authorization_endpoint": "https://dishonkadoh.com/oauth/authorize",
"token_endpoint": "https://dishonkadoh.com/oauth/token",
"jwks_uri": "https://dishonkadoh.com/.well-known/jwks.json",
"scopes_supported": ["mcp"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"]
}
Only the mcp scope exists. Only S256 PKCE is supported. Only public
clients (token_endpoint_auth_method: "none") are supported — there’s no
client-secret flow.
1. Register a client (optional, dynamic)¶
POST /api/oauth/register implements RFC 7591 Dynamic Client Registration
for public PKCE clients:
{
"client_name": "My Agent",
"redirect_uris": ["https://myapp.example.com/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
Redirect URIs must be HTTPS (localhost/127.0.0.1 exempted for local dev)
and must not contain a fragment. Returns a client_id — no secret, since
only "none" auth is supported.
3. Exchange the code for tokens¶
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<code>
&redirect_uri=<redirect_uri>
&client_id=<client_id>
&code_verifier=<pkce_verifier>
{
"access_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "<opaque_token>",
"scope": "mcp"
}
Authorization codes are single-use — a second redemption attempt (e.g. a
retried request racing the first) gets invalid_grant. Refresh tokens use
grant_type=refresh_token the same way against the same endpoint.
Token details¶
Access tokens are JWTs (typ: "at+jwt", RS256), containing:
Claim |
Value |
|---|---|
|
|
|
|
|
the portal user’s ID |
|
|
|
the registered client |
|
if available from the portal account |
|
15 minutes from issuance ( |
Refresh tokens are opaque, default TTL 30 days
(OAUTH_REFRESH_TOKEN_TTL, default 2,592,000s). Both authorization codes
and OAuth sessions/transactions are stored in Upstash Redis.
The MCP server itself (api/mcp/index.mjs) verifies incoming tokens
against the published JWKS, checking iss, aud, and that scope
contains mcp — a validly-signed token for the wrong audience or without
the mcp scope is still rejected with 401 invalid_token.
Revoking access¶
POST /oauth/revoke accepts a token (access or refresh) and invalidates
it — standard RFC 7009 token revocation.