Security and data
Authentication
Brella runs its own OAuth 2.1 server for the connector: PKCE, a real consent screen, one workspace per grant, hour-long access tokens, rotating refresh, and revocation that takes effect on the next request.
Why Brella runs its own#
The connector is protected by an OAuth 2.1 authorization server Brella runs itself, rather than by an off-the-shelf plugin. The two candidates at the time each failed on something that is not negotiable for a product holding an agency’s book: one skipped consent entirely and stored access tokens in plaintext, and the other would have forced a framework upgrade across the whole product to get it. Running it here means the consent screen is a real screen, tokens exist only as hashes, and a grant can be revoked from inside Brella OS in one tap.
- PKCE is required,
S256only. A request without a challenge, or withplain, is refused. - The issuer is configured, never derived from the request. The
Hostheader cannot move it. - No client secrets are needed. Public clients with PKCE, which is what an MCP client is.
Discovery#
Two documents, both public, both cacheable:
/.well-known/oauth-authorization-server— the endpoints, the grant types, the PKCE methods and the scopes./.well-known/oauth-protected-resource, and the path-suffixed/.well-known/oauth-protected-resource/mcpthat RFC 9728 says a client should try first for a resource with a path. Both are served.
A client does not have to fetch either: an unauthenticated POST https://www.brellapartners.com/mcp answers 401 with a WWW-Authenticate header that points straight at the protected-resource document.
Identifying the client#
There are two ways to be a client here, and neither of them involves asking us for a client id.
A client ID metadata document
The client_id IS an https URL, which we fetch and read as the client’s metadata. Claude’s hosted clients are identified this way. Because a document at a URL is self-asserted, the consent page leads with the host of that URL — not with a client_name the document chose for itself.
- The fetch is DNS-pinned and refuses any address that resolves into private space, so a client id cannot be used to make our server fetch something inside our own network.
- Claude’s two documents are also pinned as a fallback, because a CDN has been known to refuse datacenter fetches of them — a connection should not fail because someone else’s edge decided our server looked like a bot.
- A loopback redirect URI (
http://127.0.0.1, for a client like Claude Code) is matched port-agnostically, as the spec requires, since the port is chosen at runtime.
Dynamic client registration
Anything else registers itself at POST /oauth/register (RFC 7591) and gets a client id back. Registered clients are stored; a metadata-document client is fetched each time and never stored.
The authorization flow#
- The client sends the person to
GET /oauth/authorizewith its client id, redirect URI, scopes and a PKCE challenge. We record the request and send the browser on to the consent page,/connect?request=…. - The person signs in to Brella OS if they are not already. Claude never sees the password — the sign-in is on our page, in their browser.
- The consent page names the client by host, names the person by name and email, asks which one agency workspace this connection is for, and shows the two permissions with “Get work done” tickable. It also states, before the decision, that every change will be logged as their name via that app.
- A decision returns the browser to the client with a single-use authorization code. The request takes exactly one decision; a second attempt on the same request is refused.
- The client exchanges the code at
POST /oauth/token(form-encoded) with its PKCE verifier, and receives an access token and a refresh token.
A grant is a person, a workspace and a client
One person × one agency × one client × the scopes they allowed. It carries no role: the member’s role is re-read on every request, so a promotion or a demotion takes effect at once and nothing has to be reissued.
Lifetimes#
- Authorization request
How long
10 minutesDetail
The window between Claude sending you to Brella OS and you deciding. It takes exactly one decision — reload it after that and it is gone.- Authorization code
How long
60 secondsDetail
Single use. Claude exchanges it for tokens immediately. A code presented twice is treated as a stolen one: the whole connection is revoked.- Access token
How long
1 hourDetail
What each request to /mcp carries. Renewed silently in the background while you are using it.- Refresh token
How long
30 days, rotatingDetail
Rotates on every use, so the previous one dies as the new one is issued. A connection you never use lapses at 30 days and asks again.- The connection itself
How long
180 daysDetail
However often it refreshes, a connection asks you to approve it again after 180 days. Nothing stays authorised indefinitely.
Rotation, replay and retries#
Refresh tokens rotate: using one issues a new pair and retires the old one. Presenting a refresh token that has already been used is the signature of a stolen token, so it revokes the whole grant — every token under it stops working and the person reconnects.
That rule has an obvious failure mode, and it is handled. A client that sends a refresh request, loses the response to a dropped connection and retries would otherwise disconnect itself. So for 30 seconds after a rotation the successor is held, sealed, beside the token it replaced: an identical retry inside that window gets the same response back rather than a revocation. After 30 seconds, a replay is a replay.
Calling the connector#
Every call to the connector carries the access token as a bearer token:
A call to the connector
POST https://www.brellapartners.com/mcpAuthorization: Bearer <access token>Content-Type: application/jsonAccept: application/json, text/event-stream# 401 + WWW-Authenticate no token, expired, revoked, or no longer a member# 403 insufficient_scope the token lacks brella.write for this tool# 405 anything that is not POST
The member row is re-read on every request. Removal from the agency, a role change, a deactivated account — each bites on the next call, without anything being revoked by hand.
Revoking#
- From Brella OS. Settings → Claude lists every connection you hold and revokes any of them. An agency Owner can revoke any connection into the workspace.
- From the client.
POST /oauth/revoke(RFC 7009). - By resetting your password. A password reset revokes every grant you hold — a reset usually means something went wrong, and a connection that survives it would be the one thing the reset did not close.
- By time. An unused connection lapses at 30 days; every connection re-asks at 180.
Revocation takes effect on the next request, which for a stateless server is immediately.
What is stored#
- Tokens are stored as SHA-256 digests only. A token cannot be read back out of the database, by us or by anyone who reaches it.
- The grant — who, which workspace, which client, which scopes, when it was last used.
- Not your conversations. Brella never receives them. What our server logs is which action ran, for whom, and whether it succeeded.
The binding version of all of this, including retention, is the privacy policy. What reaches Claude, and what a few tools send to third parties on your behalf, is on data handling.