How OAuth 2.1 + DCR + CIMD work for MCP
The behind-the-scenes story of how brandRNA's MCP server authenticates AI clients.
Most users don't need to read this — pick your client from /docs/mcp and follow the 2-minute setup. This page is for AI builders integrating their own clients or debugging an issue.
Why OAuth for MCP
When a user connects an AI client (Claude Desktop, Cursor, etc.) to a remote MCP server, three things have to happen safely:
- Per-user attribution — usage and billing have to land on the right account, even when ten different people share the same brand of client.
- Revocability — the user must be able to cut a client off from the server without rotating any other credentials.
- Key isolation — secrets must never appear in client config files, chat transcripts, or logs.
OAuth 2.1 + PKCE solves all three. The user signs in once via the
authorization server (auth.brandrna.com), the client receives a bearer
token bound to that user, and brandRNA's MCP server validates the token on
every tool call.
The dance
sequenceDiagram
participant C as AI Client
participant M as mcp.brandrna.com
participant A as auth.brandrna.com
participant U as User browser
C->>M: POST /tools/call (no token)
M-->>C: 401 + WWW-Authenticate
C->>M: GET /.well-known/oauth-protected-resource
M-->>C: { authorization_servers: [auth.brandrna.com] }
C->>A: POST /register (DCR) or CIMD lookup
A-->>C: client_id (+ client_secret if CIMD)
C->>U: open authorize URL with PKCE + state + audience
U->>A: sign in + consent
A-->>U: redirect with code
U-->>C: code (via callback)
C->>A: POST /token (code + verifier)
A-->>C: bearer token
C->>M: POST /tools/call (Bearer ...)
M-->>C: tool resultDiscovery
Per RFC 9728, the resource
server (mcp.brandrna.com) advertises its authorization server at
/.well-known/oauth-protected-resource. The client fetches this metadata,
then talks to auth.brandrna.com for the rest of the dance.
Authorize parameters
brandRNA enforces:
- PKCE (RFC 7636) with
code_challenge_method=S256 state— a CSRF-defence nonce echoed back on redirectaudience=https://mcp.brandrna.com(RFC 8707) so tokens are bound to this resource server onlyscope=analyze:brand— the only scope on the server today
CIMD vs DCR
| Mode | When used | Example clients | Trade-off |
|---|---|---|---|
| CIMD (Client ID Metadata Documents) | Client ships a stable client_id URL pointing to its metadata document | Claude Desktop | Faster — no per-install registration round-trip; consent UI shows the canonical client identity |
| DCR (RFC 7591) | Client self-registers on first connect by POSTing metadata to /register | Cursor, Lovable, v0, Bolt | Universal — works for any compliant client, no upfront vendor registration |
Both modes converge on the same authorize → token → bearer flow. The only
difference is how the client_id is established at the start.
Per-(uid, mcp_client_id) key auto-mint
After the OAuth dance lands a fresh bearer token, brandRNA's MCP server runs a one-shot key reconciliation:
- Decode the token →
firebase_uid - Combine with the
mcp_client_id(from CIMD metadata or DCR registration) - Look up
(firebase_uid, mcp_client_id)→ existing internalbd_xxxkey - If absent, mint a fresh one and persist the mapping
The user never sees this key. It lives server-side in Firestore, scoped
to the auth-server tenant, and is rotated on revocation. This is why our MCP
config snippets contain only mcp_url — there is no plaintext bd_xxx to
paste anywhere.
Revocation
Users can revoke any connected MCP client at https://app.brandrna.com/connections. Revocation:
- Marks the
(firebase_uid, mcp_client_id)key as revoked in Firestore - Subsequent tool calls return
401immediately - Re-connecting the same client mints a fresh internal key (the user re-walks OAuth)
Tier and scope
brandRNA exposes a single OAuth scope: analyze:brand. The user's plan tier
is not baked into the token — it is read from the canonical Firestore
user document at request time, so plan upgrades take effect on the next tool
call. There is no token rotation step required to unlock higher rate limits.
Troubleshooting
- 401 with
invalid_token— the token was issued for a different audience. Check that your client passesaudience=https://mcp.brandrna.comon the authorize call. - Popup blocked /
redirect_uri_mismatch— DCR clients must register a redirect URI that matches what they pass on/authorize. The defaulthttp://127.0.0.1:<port>/callbackshape works for desktop clients. invalid_clienton/register— your DCR metadata is missingredirect_urisortoken_endpoint_auth_method.
For per-client gotchas, see the relevant client guide.