MCP

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:

  1. Per-user attribution — usage and billing have to land on the right account, even when ten different people share the same brand of client.
  2. Revocability — the user must be able to cut a client off from the server without rotating any other credentials.
  3. 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 result

Discovery

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 redirect
  • audience=https://mcp.brandrna.com (RFC 8707) so tokens are bound to this resource server only
  • scope=analyze:brand — the only scope on the server today

CIMD vs DCR

ModeWhen usedExample clientsTrade-off
CIMD (Client ID Metadata Documents)Client ships a stable client_id URL pointing to its metadata documentClaude DesktopFaster — 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 /registerCursor, Lovable, v0, BoltUniversal — 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:

  1. Decode the token → firebase_uid
  2. Combine with the mcp_client_id (from CIMD metadata or DCR registration)
  3. Look up (firebase_uid, mcp_client_id) → existing internal bd_xxx key
  4. 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 401 immediately
  • 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 passes audience=https://mcp.brandrna.com on the authorize call.
  • Popup blocked / redirect_uri_mismatch — DCR clients must register a redirect URI that matches what they pass on /authorize. The default http://127.0.0.1:<port>/callback shape works for desktop clients.
  • invalid_client on /register — your DCR metadata is missing redirect_uris or token_endpoint_auth_method.

For per-client gotchas, see the relevant client guide.

On this page