Skip to content

LLM-733: claude.ai connector with only the URL — CIMD/DCR public clients and a login on /authorize - #376

Merged
jeffdafoe merged 3 commits into
mainfrom
LLM-733-claude-ai-login
Oct 9, 2026
Merged

jeffdafoe merged 3 commits into
mainfrom
LLM-733-claude-ai-login

Conversation

@jeffdafoe

Copy link
Copy Markdown
Owner

LLM-733

A claude.ai user can now add the connector with only https://llm-memory.net/mcp, click Connect, and log in with agent name + password. No Client ID / Client Secret.

Why CIMD first, DCR second (Anthropic connector docs, claude.com/docs/connectors/building/authentication): with Advanced settings empty, claude.ai uses a Client ID Metadata Document when the authorization-server metadata advertises both client_id_metadata_document_supported: true and "none" as a token auth method, and otherwise falls back to DCR. DCR "registers a new client on every fresh connection". claude.ai also probes /.well-known/oauth-protected-resource/mcp first (seen in our request_log, 404 until now).

Discovery

  • AS metadata: registration_endpoint = /oauth/register (/register is the signup page), token_endpoint_auth_methods_supported: ["none", "client_secret_post"], client_id_metadata_document_supported: true.
  • New /.well-known/oauth-protected-resource/mcp with resource = <base>/mcp. The root form is unchanged.

Public clients (services/oauth-public-clients.js)

  • CIMD: client_id must be an https URL on https://claude.ai or https://claude.com with a path. The document is not fetched: the redirect_uri is held to our fixed allowlist anyway, and fetching would mean an outbound request to a caller-chosen URL.
  • DCR: POST /oauth/register (JSON) accepts only allowlisted callbacks and returns a public client (token_endpoint_auth_method: none, grant_types: [authorization_code]). The client_id is dcr. + base64url({r: redirect_uris, t}) + . + HMAC (bearer secret, domain-separated label). No storage, survives restarts. /authorize holds a DCR client to the callbacks it registered.

Login on /authorize

  • GET with a public client: a login page (agent name + password) with the load-memory block above the form. X-Frame-Options: DENY, frame-ancestors 'none', no-store.
  • POST /authorize (form): re-checks the whole OAuth request (hidden fields are untrusted), then rate limits (5 tries per agent name, 20 per client address per 15 min; the address is the last X-Forwarded-For hop, as in request-log.js), then verifyPasswordLogin (timing-safe for unknown names). Failure re-renders with the name kept and the password never echoed. Success: a 60 s code bound to the agent who logged in, then 303 to the callback.
  • POST with an agent-name client_id is refused.

Token endpoint

  • A code with a bound agent: PKCE + matching client_id/redirect_uri, no secret, issues that agent's usual HMAC token.
  • An agent-name code still requires its client_secret (now checked after the code is read; 401 invalid_client when missing).

Unchanged: the Client ID / Client Secret path (wendy, remi, …). https://claude.com/api/mcp/auth_callback is added to the allowlist; the docs say claude.ai's callback may move there.

Other

  • services/password-login.js: the dashboard login's name + password check, now shared by /admin/login and the connector login.
  • Signup guide (claude.ai step 1, API-key hint) and MEM-151 (getting-started template: exact-text patch of two passages, RAISEs if either is missing; existing accounts' notes untouched). Tested on a copy of the live template: up, rerun refused, down restores the exact original.

Known limit (unchanged, out of scope): the per-agent token never expires, so a login cannot be revoked short of rotating mcp_oauth_bearer_secret, same as the secret path today.

Tests: oauth-public-client.test.js (16): metadata, DCR accept/refuse, CIMD origin rules, forged/tampered DCR ids, DCR callback binding, wrong/unknown login, CIMD and DCR end-to-end exchange, wrong client/callback/verifier, agent-name code still needs a secret, POST refuses agent-name clients, per-name and per-address limits. password-login.test.js (3). Existing oauth.test.js unchanged and passing. Probes: removing the DCR callback check or the public-code branch fails the tests. Full suite 312 pass, 2 skipped (opt-in Postgres tests).

Not yet tried from a real claude.ai account — that is the first post-deploy check.

Agent: Home

🤖 Generated with Claude Code

jeffdafoe and others added 3 commits October 9, 2026 17:40
…nts and a login on /authorize

5 of 13 outside accounts never connected a client, and the claude.ai setup
asked them to paste their agent name as Client ID and API key as Client
Secret from a one-time screen. Now the connector is added with only the URL.

- OAuth metadata advertises CIMD (client_id_metadata_document_supported +
  "none") and a registration_endpoint; the path-form protected-resource
  metadata (/.well-known/oauth-protected-resource/mcp) is served.
- CIMD: client_id URLs on https://claude.ai or https://claude.com only; the
  document is not fetched (redirect_uri stays on our fixed allowlist).
- DCR at POST /oauth/register: allowed callbacks only; the client_id carries
  its own HMAC-signed registration, so nothing is stored.
- /authorize for these clients shows a login (agent name + dashboard
  password, POSTed, never in a URL), framed nowhere, with the load-memory
  block. 5 tries per agent name and 20 per client address per 15 min. The
  code is bound to the agent that logged in; the token endpoint takes PKCE
  without a secret for it and issues that agent's token.
- The Client ID / Client Secret path is unchanged. claude.com callback added.
- Dashboard login and connector login share services/password-login.js.
- Signup guide and getting-started template (MEM-151, exact-text patch) say
  "add the URL, click Connect, log in".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jeffdafoe
jeffdafoe merged commit 3f24201 into main Oct 9, 2026
8 of 9 checks passed
@jeffdafoe
jeffdafoe deleted the LLM-733-claude-ai-login branch October 9, 2026 21:52
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