DocsDocs
Open app

OAuth authentication

OAuth 2.0 with PKCE for custom MCP integrations.

The Doow MCP server uses OAuth 2.0 with PKCE for authentication. Most MCP clients handle this automatically. This page documents the flow for custom integrations.

Discovery endpoints

http
GET https://mcp.doow.co/.well-known/oauth-authorization-serverGET https://mcp.doow.co/.well-known/oauth-protected-resource

These return the OAuth metadata including endpoints, supported grants, and PKCE requirements.

Flow overview

text
1. Register client       POST /oauth/register2. Build authorize URL   GET  /oauth/authorize3. User signs in         (browser)4. Handle callback       GET  your-redirect-uri?code=...5. Exchange code         POST /oauth/token6. Call MCP endpoint     POST /mcp (with Bearer token)7. Refresh token         POST /oauth/token (grant_type=refresh_token)

1. Client registration

Register your client with Dynamic Client Registration (RFC 7591):

bash
curl -X POST https://mcp.doow.co/oauth/register \-H "Content-Type: application/json" \-d '{  "client_name": "My MCP Client",  "redirect_uris": ["http://127.0.0.1:8080/callback"],  "grant_types": ["authorization_code", "refresh_token"],  "response_types": ["code"],  "token_endpoint_auth_method": "none",  "scope": "mcp:read mcp:write"}'

Response:

json
{"client_id": "mcp_abc123...","client_id_issued_at": 1725638400,"redirect_uris": ["http://127.0.0.1:8080/callback"],"grant_types": ["authorization_code", "refresh_token"],"response_types": ["code"],"scope": "mcp:read mcp:write","token_endpoint_auth_method": "none","pkce_required": true}

2. Build authorize URL

Generate a PKCE code verifier and challenge:

javascript
// Generate 43-128 character verifierconst verifier = crypto.randomBytes(32).toString('base64url');// Create S256 challengeconst challenge = crypto.createHash('sha256').update(verifier).digest('base64url');

Build the authorization URL with all required parameters:

http
https://mcp.doow.co/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=http://127.0.0.1:8080/callback&scope=mcp:read%20mcp:write&state=RANDOM_STATE&code_challenge=YOUR_CHALLENGE&code_challenge_method=S256&resource=https://mcp.doow.co

3. Handle callback

After the user authorizes, they are redirected to your registered callback URL:

http
http://127.0.0.1:8080/callback?code=AUTH_CODE&state=YOUR_STATE

Verify the state matches what you sent, then extract the code.

4. Exchange code for token

bash
curl -X POST https://mcp.doow.co/oauth/token \-H "Content-Type: application/x-www-form-urlencoded" \-d "grant_type=authorization_code" \-d "code=AUTH_CODE" \-d "redirect_uri=http://127.0.0.1:8080/callback" \-d "client_id=YOUR_CLIENT_ID" \-d "code_verifier=YOUR_VERIFIER"

Response:

json
{"access_token": "eyJ...","token_type": "Bearer","expires_in": 3600,"refresh_token": "mcp_rt_...","scope": "mcp:read mcp:write"}

5. Call MCP endpoint

Initialize an MCP session:

bash
curl -X POST https://mcp.doow.co/mcp \-H "Authorization: Bearer ACCESS_TOKEN_JWT" \-H "Content-Type: application/json" \-H "Accept: application/json, text/event-stream" \-d '{  "jsonrpc": "2.0",  "method": "initialize",  "params": {    "protocolVersion": "2025-03-26",    "capabilities": {},    "clientInfo": { "name": "my-client", "version": "1.0.0" }  },  "id": 1}'

The response includes an Mcp-Session-Id header. Include this header on all subsequent requests.

6. Refresh token

Access tokens expire after 1 hour, so refresh them before they expire:

bash
curl -X POST https://mcp.doow.co/oauth/token \-H "Content-Type: application/x-www-form-urlencoded" \-d "grant_type=refresh_token" \-d "refresh_token=mcp_rt_..." \-d "client_id=YOUR_CLIENT_ID"

Refresh tokens last 30 days of non-use, and using one extends its lifetime.

After refreshing an access token, initialize a new MCP session. The current server does not rebind an existing session to the refreshed token.

Token revocation

Revoke tokens when you are done:

bash
curl -X POST https://mcp.doow.co/oauth/revoke \-H "Content-Type: application/x-www-form-urlencoded" \-d "token=ACCESS_TOKEN_JWT" \-d "client_id=YOUR_CLIENT_ID"

Common mistakes

MistakeFix
Missing resource parameterAdd resource=https://mcp.doow.co to authorize URL
Short code verifierUse 43-128 characters, not the common default of 32
Base64 padding in challengeUse base64url encoding without trailing =
Mixing localhost and 127.0.0.1Use the same form in registration and authorize URL
Regenerating verifierUse the same verifier for challenge and token exchange

Rate limits

EndpointLimit
/oauth/register10 per hour per IP
/oauth/tokenNo MCP-specific limiter currently wired
/oauth/token (refresh)No MCP-specific limiter currently wired
/mcp (OAuth member)60 per minute per member
/mcp (MCP API key)30 per minute per member
/mcp (organization)500 per minute per organization
Tool callsTool-specific limits apply where declared; many are 10 per minute

Rate limit responses include a Retry-After header, while tokens use these formats:

  • OAuth access tokens are raw JWTs.
  • Refresh tokens start with mcp_rt_.
  • MCP API keys start with doow_mcp_. See the MCP API key section for details.

Next steps

Was this page helpful?