Skip to content

List a connection's keys and revoke one without revoking the connection - #53

Merged
shuv1337 merged 1 commit into
v2from
shuv/connection-key-revoke-20261022
Oct 23, 2026
Merged

shuv1337 merged 1 commit into
v2from
shuv/connection-key-revoke-20261022

Conversation

@shuv1337

Copy link
Copy Markdown
Owner

Post-cutover backlog, item (d): list a connection's keys and revoke one, without revoking the connection.

Rotating a 90-day seat key on the connection page meant revoking the whole connection. That killed the seat's current key too, and today it also burns the connection ID (see the separate ID-reuse fix). The account-wide Tokens page already lists connection keys with their own Revoke button. This PR puts the same control where operators manage the connection.

API (hosted)

  • GET /api/organizations/:org/mcp-connections/:connection/keys returns, newest first, each key's id, name, start (its first characters), createdAt, expiresAt and lastRequest. It never returns key material.
  • POST /api/organizations/:org/mcp-connections/:connection/keys/:key/revoke deletes that one key.
  • Who can call them: the same rules as minting. Only the connection's owner; admins and owners can call them with an API key, but only for their own connections.
  • Refusals: another user's connection, or a revoked one, returns 404 ConnectionNotFound. A key that isn't bound to that connection returns 404 ConnectionKeyNotFound, which is new.
  • Implementation: both are server-only Better Auth endpoints. The connection must be standing and owned by the caller, and the key's metadata must bind it to that connection. Each revocation writes an audit line. Bearer checks read the key row, so a revoked key fails immediately while the connection and its other keys stay.

UI

The connection page gets a Connection keys section showing each key's name, first characters, created, expires and last used, with a per-key Revoke and a confirm dialog. Creating a key refreshes the list.

Tests

New connectionKeyRevoke scenario in connection-keys.spec.ts:

  • two keys are listed without secrets, and another connection lists none of them;
  • an admin and the owner each get 404 on someone else's connection;
  • the key itself gets 403 on these routes;
  • after revoking one key, it gets 401 and fails over MCP, while the other key and the connection keep working, and a second revoke returns 404;
  • the dashboard revoke works.

With the server-side delete removed, the scenario fails as it should. On self-host, the 9 related scenarios pass (connection-keys, connection-api-keys, mcp-connections), and bun run check passes.

connection-keys.spec.ts
connection-api-keys.spec.ts
mcp-connections.spec.ts

Operators rotating a seat's 90-day connection key had no way to retire the old key short of
revoking the whole connection, which also killed the current key and burned the connection id.

GET .../mcp-connections/<id>/keys lists a connection's keys (id, name, first characters, created,
expiry, last use; never the secret) and POST .../mcp-connections/<id>/keys/<key>/revoke deletes
one. Both run through new server-only auth endpoints that, like minting, act only on the caller's
own standing connection and report any other as ConnectionNotFound; a key that is not bound to
that connection is ConnectionKeyNotFound. Admin API credentials follow the mint rules, and every
revocation logs an mcp-connection-key audit line. Bearer checks read the key row on every request,
so the deleted key is refused at once while the connection and its other keys keep working.

The connection's dashboard page lists its keys with a per-key Revoke and a confirm dialog; minting
refreshes the list.

connection-keys.spec gains connectionKeyRevoke: two keys listed without secrets, admin and org
owner get 404 listing/revoking, the key itself is refused by the API, revoking through another
connection is ConnectionKeyNotFound, the revoked key gets 401 at the connection URL while the
other works and the connection stays, the audit line, and a dashboard revoke.
@shuv1337
shuv1337 merged commit 07e5ade into v2 Oct 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant