fix(oauth): allow generic RFC 7591 DCR + expand install snippets
All checks were successful
Deploy to Production / deploy (push) Successful in 1m28s

- /oauth/register: drop resource_required check, accept generic
  registrations (Claude Desktop omits resource in DCR body per spec).
  serverId stored as NULL; /authorize still enforces org-ownership
  + access-token aud claim still pinned to resource. Fixes Claude
  Desktop DCR failure (ofid_d7e39530c109fa7f).
- /oauth/authorize: skip strict server.id check when client.serverId
  is NULL (generic client); org check remains the security boundary.
- schema: oauth_clients.server_id no longer NOT NULL.
- migration 0002: ALTER COLUMN server_id DROP NOT NULL (already
  applied on prod).
- install-snippets: add Claude Code (CLI), VS Code, Codex, raw URL
  tabs. Claude Desktop now shows form-field values (Name / Remote MCP
  Server URL / OAuth Client ID / Secret) matching the new Custom
  Connector UI instead of the obsolete JSON config.
- types: InstallTarget enum extended.
- hero-video: clicking the audio toggle restarts the video from
  frame 0 so unmute aligns with the spoken opening.
- marketing: drop em-dashes from rendered copy.
This commit is contained in:
Marco Sadjadi
2026-05-28 17:20:01 +02:00
parent e75f9ad4fe
commit 3a05766f88
9 changed files with 172 additions and 38 deletions

View File

@@ -0,0 +1,8 @@
-- Allow generic RFC 7591 Dynamic Client Registration:
-- a client may register without binding to a specific MCP server.
-- /oauth/authorize still enforces the org-ownership check on every
-- authorization, and the access-token `aud` claim is pinned to the
-- resource declared at /token, so a generic client cannot mint a
-- token usable against a server outside the user's org.
ALTER TABLE oauth_clients
ALTER COLUMN server_id DROP NOT NULL;

View File

@@ -260,9 +260,13 @@ export const secrets = pgTable('secrets', {
export const oauthClients = pgTable('oauth_clients', {
id: uuid('id').defaultRandom().primaryKey(),
serverId: uuid('server_id')
.references(() => mcpServers.id, { onDelete: 'cascade' })
.notNull(),
// Nullable: RFC 7591 Dynamic Client Registration treats the `resource`
// claim as optional, so a client may register generically and only bind
// to a specific server at /oauth/authorize. /authorize enforces the
// org-ownership check on every authorization either way.
serverId: uuid('server_id').references(() => mcpServers.id, {
onDelete: 'cascade',
}),
clientId: varchar('client_id', { length: 128 }).notNull().unique(),
clientSecretHash: text('client_secret_hash'),
redirectUris: jsonb('redirect_uris').notNull(),