[PR #9159] [MERGED] feat(cimd): add Client ID Metadata Document plugin #25374

Closed
opened 2026-04-15 22:51:31 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/9159
Author: @gustavovalverde
Created: 4/13/2026
Status: Merged
Merged: 4/13/2026
Merged by: @gustavovalverde

Base: nextHead: 2026-04-13/feat/cimd-plugin


📝 Commits (5)

  • 0efedba feat(oauth-provider): rework CIMD to Client ID Metadata Document
  • d79ed31 refactor(cimd): reshape into standalone plugin with clientDiscovery contract
  • e91afeb fix(demo): sync nextjs lockfile with @better-auth/cimd link
  • 9f10076 docs(cimd): fix stale clientResolver reference and clarity pass
  • adc18d1 fix(cimd): address review feedback on memory bounds, SSRF coverage, and test headers

📊 Changes

33 files changed (+3383 additions, -44 deletions)

View changed files

.changeset/cimd-plugin.md (+58 -0)
📝 .changeset/pre.json (+2 -0)
📝 .cspell/auth-terms.txt (+1 -0)
📝 demo/nextjs/lib/auth.ts (+2 -0)
📝 demo/nextjs/package.json (+1 -0)
📝 demo/nextjs/pnpm-lock.yaml (+48 -24)
docs/content/docs/plugins/cimd.mdx (+203 -0)
📝 docs/content/docs/plugins/meta.json (+1 -0)
📝 docs/content/docs/plugins/oauth-provider.mdx (+9 -2)
📝 e2e/smoke/package.json (+2 -0)
e2e/smoke/test/cimd.spec.ts (+325 -0)
packages/cimd/README.md (+17 -0)
packages/cimd/package.json (+70 -0)
packages/cimd/src/cimd.test.ts (+277 -0)
packages/cimd/src/client-store.ts (+374 -0)
packages/cimd/src/flows.test.ts (+536 -0)
packages/cimd/src/index.ts (+79 -0)
packages/cimd/src/resolver.ts (+115 -0)
packages/cimd/src/types.ts (+66 -0)
packages/cimd/src/validate-metadata-document.test.ts (+501 -0)

...and 13 more files

📄 Description

Adds @better-auth/cimd for unauthenticated client discovery via Client ID Metadata Documents, the mechanism MCP uses to discover clients without prior registration. Clients identify themselves by hosting an HTTPS document whose URL becomes their client_id; the server fetches, validates, and caches it on first use.

To compose the plugin without mutating host internals, @better-auth/oauth-provider gains a typed clientDiscovery extension point on OAuthOptions. Entries implement a ClientDiscovery contract (id, matches, resolve, discoveryMetadata); getClient() walks them after the database lookup and returns the first non-null resolve. Each entry contributes fields merged into /.well-known/oauth-authorization-server and /.well-known/openid-configuration.

Highlights

  • RFC-compliant §3 and §4.1 validation; SSRF protection covering RFC 6890 private ranges, IPv4-mapped IPv6 in both dotted and hex forms, and cloud metadata hostnames. Runtime-agnostic: no DNS resolution, so it runs identically on Node, Bun, Deno, and Workers.
  • allowFetch(url, ctx) pre-fetch gate for operator origin allowlists, per-host rate limiting, or DNS-level defenses.
  • Admin-set flags (disabled, skipConsent, enableEndSession) preserved across stale refreshes.
  • Same-origin jwks_uri trust when the client_id is itself an HTTPS URL, so private_key_jwt CIMD clients work without custom trustedOrigins plumbing.
  • Content-Type guard (application/json or application/*+json), 5 s fetch timeout, 5 KB UTF-8 byte response limit, no redirect following.

Closes #7185 (close when this merges)
Closes #7184


Summary by cubic

Adds @better-auth/cimd for unauthenticated client discovery via Client ID Metadata Documents (CIMD) and a clientDiscovery extension in @better-auth/oauth-provider, with discovery support advertised in well-known metadata. Hardens fetch limits and SSRF protection for safer URL client_id resolution.

  • New Features

    • @better-auth/cimd: URL client_id discovery with RFC §3/§4.1 validation, SSRF guards, JSON-only, 5s timeout, 5KB limit, no redirects; stream-enforced body cap (early abort on Content-Length > cap) and expanded SSRF ranges (192.88.99.0/24, 224/4, 240/4).
    • Origin binding for redirect/post-logout/client URIs; localhost allowed only for redirect URIs.
    • Hooks and behavior: allowFetch pre-fetch gate, periodic refresh, preserves admin flags; supports token_endpoint_auth_method "none" and "private_key_jwt" (inline or same-origin jwks_uri).
    • @better-auth/oauth-provider: new clientDiscovery; getClient() tries DB then discovery; merges discovery data into /.well-known/*; advertises public_client_supported and client_id_metadata_document_supported; trusts same-origin jwks_uri for URL clients; exports checkOAuthClient and oauthToSchema.
  • Migration

    • Install @better-auth/cimd and add cimd() to your plugins after oauthProvider(...).
    • Optionally configure allowFetch, refreshRate, and originBoundFields.
    • No changes required for existing registered clients.

Written for commit adc18d132d. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/9159 **Author:** [@gustavovalverde](https://github.com/gustavovalverde) **Created:** 4/13/2026 **Status:** ✅ Merged **Merged:** 4/13/2026 **Merged by:** [@gustavovalverde](https://github.com/gustavovalverde) **Base:** `next` ← **Head:** `2026-04-13/feat/cimd-plugin` --- ### 📝 Commits (5) - [`0efedba`](https://github.com/better-auth/better-auth/commit/0efedba1cf44c252849213d05fea22cc285e62ad) feat(oauth-provider): rework CIMD to Client ID Metadata Document - [`d79ed31`](https://github.com/better-auth/better-auth/commit/d79ed312ff7d2c7bec0dae3879a5245846d0294a) refactor(cimd): reshape into standalone plugin with clientDiscovery contract - [`e91afeb`](https://github.com/better-auth/better-auth/commit/e91afeb27ab957d167156ee44db805f963638e10) fix(demo): sync nextjs lockfile with @better-auth/cimd link - [`9f10076`](https://github.com/better-auth/better-auth/commit/9f10076d1a8433b42fd2d0f546db9c5ea811feab) docs(cimd): fix stale clientResolver reference and clarity pass - [`adc18d1`](https://github.com/better-auth/better-auth/commit/adc18d132d4f6d9e49de94058b2acc277df8e10e) fix(cimd): address review feedback on memory bounds, SSRF coverage, and test headers ### 📊 Changes **33 files changed** (+3383 additions, -44 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/cimd-plugin.md` (+58 -0) 📝 `.changeset/pre.json` (+2 -0) 📝 `.cspell/auth-terms.txt` (+1 -0) 📝 `demo/nextjs/lib/auth.ts` (+2 -0) 📝 `demo/nextjs/package.json` (+1 -0) 📝 `demo/nextjs/pnpm-lock.yaml` (+48 -24) ➕ `docs/content/docs/plugins/cimd.mdx` (+203 -0) 📝 `docs/content/docs/plugins/meta.json` (+1 -0) 📝 `docs/content/docs/plugins/oauth-provider.mdx` (+9 -2) 📝 `e2e/smoke/package.json` (+2 -0) ➕ `e2e/smoke/test/cimd.spec.ts` (+325 -0) ➕ `packages/cimd/README.md` (+17 -0) ➕ `packages/cimd/package.json` (+70 -0) ➕ `packages/cimd/src/cimd.test.ts` (+277 -0) ➕ `packages/cimd/src/client-store.ts` (+374 -0) ➕ `packages/cimd/src/flows.test.ts` (+536 -0) ➕ `packages/cimd/src/index.ts` (+79 -0) ➕ `packages/cimd/src/resolver.ts` (+115 -0) ➕ `packages/cimd/src/types.ts` (+66 -0) ➕ `packages/cimd/src/validate-metadata-document.test.ts` (+501 -0) _...and 13 more files_ </details> ### 📄 Description Adds `@better-auth/cimd` for unauthenticated client discovery via [Client ID Metadata Documents](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/), the mechanism [MCP](https://modelcontextprotocol.io/specification/draft/basic/authorization#client-id-metadata-documents-flow) uses to discover clients without prior registration. Clients identify themselves by hosting an HTTPS document whose URL becomes their `client_id`; the server fetches, validates, and caches it on first use. To compose the plugin without mutating host internals, `@better-auth/oauth-provider` gains a typed `clientDiscovery` extension point on `OAuthOptions`. Entries implement a `ClientDiscovery` contract (`id`, `matches`, `resolve`, `discoveryMetadata`); `getClient()` walks them after the database lookup and returns the first non-null resolve. Each entry contributes fields merged into `/.well-known/oauth-authorization-server` and `/.well-known/openid-configuration`. ### Highlights - RFC-compliant §3 and §4.1 validation; SSRF protection covering RFC 6890 private ranges, IPv4-mapped IPv6 in both dotted and hex forms, and cloud metadata hostnames. Runtime-agnostic: no DNS resolution, so it runs identically on Node, Bun, Deno, and Workers. - `allowFetch(url, ctx)` pre-fetch gate for operator origin allowlists, per-host rate limiting, or DNS-level defenses. - Admin-set flags (`disabled`, `skipConsent`, `enableEndSession`) preserved across stale refreshes. - Same-origin `jwks_uri` trust when the `client_id` is itself an HTTPS URL, so `private_key_jwt` CIMD clients work without custom `trustedOrigins` plumbing. - Content-Type guard (`application/json` or `application/*+json`), 5 s fetch timeout, 5 KB UTF-8 byte response limit, no redirect following. Closes #7185 (close when this merges) Closes #7184 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds `@better-auth/cimd` for unauthenticated client discovery via Client ID Metadata Documents (CIMD) and a `clientDiscovery` extension in `@better-auth/oauth-provider`, with discovery support advertised in well-known metadata. Hardens fetch limits and SSRF protection for safer URL `client_id` resolution. - New Features - `@better-auth/cimd`: URL `client_id` discovery with RFC §3/§4.1 validation, SSRF guards, JSON-only, 5s timeout, 5KB limit, no redirects; stream-enforced body cap (early abort on Content-Length > cap) and expanded SSRF ranges (192.88.99.0/24, 224/4, 240/4). - Origin binding for redirect/post-logout/client URIs; localhost allowed only for redirect URIs. - Hooks and behavior: `allowFetch` pre-fetch gate, periodic refresh, preserves admin flags; supports `token_endpoint_auth_method` "none" and "private_key_jwt" (inline or same-origin `jwks_uri`). - `@better-auth/oauth-provider`: new `clientDiscovery`; `getClient()` tries DB then discovery; merges discovery data into `/.well-known/*`; advertises `public_client_supported` and `client_id_metadata_document_supported`; trusts same-origin `jwks_uri` for URL clients; exports `checkOAuthClient` and `oauthToSchema`. - Migration - Install `@better-auth/cimd` and add `cimd()` to your plugins after `oauthProvider(...)`. - Optionally configure `allowFetch`, `refreshRate`, and `originBoundFields`. - No changes required for existing registered clients. <sup>Written for commit adc18d132d4f6d9e49de94058b2acc277df8e10e. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 22:51:31 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#25374