OAuth + DCR
Dock's MCP server speaks OAuth 2.1 with Dynamic Client Registration (DCR). MCP-aware clients (Claude Desktop, Claude Code, Cursor, Windsurf, Cline) configure themselves automatically, no API key copy-paste, no manual client-id registration.
Endpoint
https://trydock.ai/api/mcpSingle endpoint, server-sent events for streaming responses, JSON-RPC for requests. MCP clients fetch the OAuth metadata on first connect from the two well-known URLs below.
Discovery URLs
Two metadata documents, one per RFC. Both are public, unauthenticated, edge-cached, CORS-open.
/.well-known/oauth-authorization-server, RFC 8414 authorization-server metadata. Returns the authorization endpoint, token endpoint, registration endpoint, supported scopes, grant types, and PKCE methods./.well-known/oauth-protected-resource, RFC 9728 protected-resource metadata. Names/api/mcpas the resource and points back at the authorization server.
Both endpoints accept any path suffix. ChatGPT's MCP client appends the resource segment when it discovers metadata, so it requests /.well-known/oauth-authorization-server/mcp rather than the spec-compliant root path. A wildcard child route serves the same JSON regardless of suffix, so spec-compliant clients (Claude Desktop, Cursor, Windsurf, Cline) and ChatGPT both work.
Dynamic Client Registration
Per RFC 7591. On first connect, the client POSTs to the registration endpoint with its name + redirect URIs + grant types. The server returns a client_id + client_secret. The client stores these and uses them for the OAuth flow.
For Dock, every registration is automatic, no manual approval. Each registration is bound to your user account, visible at /settings/connected-clients. Revoke any client there to immediately invalidate its tokens.
Auth flow (PKCE)
- Client posts DCR registration → gets client_id.
- Client opens browser to
/api/mcp/oauth/authorize?client_id=…&code_challenge=… - User signs in (Google, emailed link, or an existing session) + approves the scope grant.
- Browser redirects to client's redirect_uri with an authorization code.
- Client exchanges code for access_token + refresh_token via
/api/mcp/oauth/token. - Subsequent JSON-RPC calls to
/api/mcpinclude the access token inAuthorization: Bearer.
Scopes
workspaces:read: list, get workspace details, read rows + doc bodiesworkspaces:write: create workspaces, add/edit rows, write doc bodiesmembers:manage: invite humans + agents, change rolesbilling:read: view current plan + usagebilling:write: upgrade/downgrade/request limit increase (gated by dangerous-ops handshake)
Refresh tokens
Access tokens expire in 1 hour. Refresh tokens are valid for 30 days; using one rotates it (sliding expiry). If a refresh token is older than 30 days unused, the user must re-authorize via the browser.
Attribution: one connection, one identity
How a call is attributed depends on the credential it carries:
- A per-agent API key (
dk_…) attributes every call to that agent. The credential IS the identity, server-verified. - An account-level OAuth connection attributes every call to a single principal: the OAuth-promoted agent for that client, or the account owner if none. One OAuth connection cannot tell apart multiple agents multiplexed over it.
If you run multiple agents and want each one's actions attributed correctly (activity feed, audit log, created by fields), give each agent its own dk_API key and connect it as that agent's MCP bearer. One MCP connection per agent. A single shared account connection has no authenticated way to tell which agent made a given call, so it cannot attribute per-agent.
To mint a per-agent dk_ key, see /docs/agents/agent-auth (Path 1).
Revoking access
- User-side: visit /settings/connected-clients, click revoke on the client. Effective within 60 seconds.
- Programmatic:
DELETE /api/mcp/oauth/clients/:id. - Mass-revoke (compromise scenario):
DELETE /api/me/sessionsrevokes everything in the same call (all OAuth tokens, all DCR clients, all browser sessions).
Frequently asked questions
- Does Dock support OAuth 2.1 with Dynamic Client Registration?
- Yes. Discovery at `/.well-known/oauth-authorization-server`, registration at `/api/oauth/register`, authorization at `/api/oauth/authorize`, token at `/api/oauth/token`. Spec-compliant clients (Claude Desktop, Claude Code, ChatGPT, Cursor) auto-register without manual setup.
- Where do I find Dock's OAuth discovery endpoints?
- Two of them: `https://trydock.ai/.well-known/oauth-authorization-server` (RFC 8414, returns auth endpoint, token endpoint, registration endpoint, supported scopes, grant types, PKCE methods) and `https://trydock.ai/.well-known/oauth-protected-resource` (RFC 9728, names `/api/mcp` as the resource). MCP clients hit both automatically; you only need them for custom integrations.
- Why does my MCP client request `/.well-known/oauth-authorization-server/mcp`?
- ChatGPT's MCP client appends the resource path segment to the well-known URL when it discovers OAuth metadata. The spec-compliant URL is the root path, but ChatGPT requests the suffixed one. Dock serves the same JSON at both, a wildcard child route catches the suffix and returns identical metadata. Spec-compliant clients (Claude Desktop, Cursor, Windsurf, Cline) and ChatGPT both work.
- Does Dock's OAuth flow require PKCE?
- Yes. Per OAuth 2.1, all clients (including confidential clients) must use PKCE with S256. The `code_challenge` and `code_verifier` flow is enforced server-side; clients that omit them get rejected at the authorize step.
- What scopes does Dock's OAuth server support?
- `mcp` is the single scope today (covers all MCP tool calls under the user's accessible workspaces). Per-tool scopes are on the roadmap. The user's role on each workspace is the existing access gate; OAuth doesn't add a separate scope grid.
- How does Dock's Dynamic Client Registration work?
- Clients POST to `/api/oauth/register` with their client metadata (name, redirect_uris). Dock returns a `client_id` immediately (no admin approval). The client uses that `client_id` for the rest of the OAuth flow. Reduces friction; no manual app-registration step.
- How do I refresh an expired Dock OAuth access token?
- POST to `/api/oauth/token` with `grant_type=refresh_token` and the `refresh_token` you got at the original token grant. Returns a new `access_token` (and a new `refresh_token` if rotating). Standard OAuth 2.1 refresh flow.
- I connected via OAuth and all my agents show as one identity — why?
- Account-OAuth is one credential = one principal. The OAuth connection authenticates the account (or the OAuth-promoted agent for the client); it has no way to multiplex multiple distinct agent identities over the same connection. If you want each agent attributed correctly in the activity feed, audit log, and `createdBy` fields, give each agent its own `dk_` API key and connect it as that agent's MCP bearer. See `/docs/mcp/oauth#attribution` for the full attribution model. Learn more →
- Can I revoke a Dock OAuth access token?
- Yes. POST to `/api/oauth/revoke` with the token. The token's `tokensRevokedAt` field is set; subsequent requests with it return 401. The user can also revoke from Settings → Authorized apps in the dashboard, which revokes every token + the client registration. Revocation cascades: when an OAuth token is revoked, the OAuth-promoted Agent it produced (via the /oauth/token auto-promote step) is archived and its WorkspaceMember access removed in the same transaction. Explicit `dk_` API keys are NOT touched by OAuth revoke — those are revoked separately via `DELETE /api/keys/<id>`, so revoking an OAuth client doesn't kick an unrelated long-running agent.
- How long do Dock OAuth tokens live?
- Access tokens: 1 hour. Refresh tokens: 90 days, rotated on each refresh. Authorization codes: 60 seconds. Conservative defaults that match typical MCP-client expectations and minimize blast radius if a token leaks.
- Does Dock OAuth support PKCE-only public clients?
- Yes. Public clients (no client_secret) are first-class. PKCE is the security substitute. Useful for desktop apps and CLI tools that can't safely store a client_secret. Confidential clients (with secret) are also supported for server-side integrations.
- How do I debug a Dock OAuth flow that's failing?
- Check the `error` + `error_description` query params on the redirect back to your client. Common: invalid_redirect_uri (mismatch with what you registered), invalid_grant (code expired or already used), invalid_scope (Dock only supports `mcp` today). The discovery endpoint is the canonical source of supported values.
Related
- MCP overview: what MCP is + why we use it.
- Connecting clients: per-client setup steps.