[PR #7185] feat(oauth-provider): add Client ID Metadata Document support for dynamic client discovery #15376

Open
opened 2026-04-13 09:59:45 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/7185
Author: @dvanmali
Created: 1/7/2026
Status: 🔄 Open

Base: nextHead: cimd


📝 Commits (1)

  • 2ec168b feat(oauth-provider): rework CIMD to Client ID Metadata Document

📊 Changes

14 files changed (+1607 additions, -9 deletions)

View changed files

.changeset/oauth-provider-client-id-metadata-document.md (+40 -0)
📝 .cspell/auth-terms.txt (+1 -0)
📝 demo/nextjs/lib/auth.ts (+1 -0)
📝 docs/content/docs/plugins/oauth-provider.mdx (+40 -2)
packages/oauth-provider/src/cimd.test.ts (+276 -0)
📝 packages/oauth-provider/src/metadata.ts (+6 -1)
📝 packages/oauth-provider/src/oauth.ts (+2 -0)
📝 packages/oauth-provider/src/types/index.ts (+60 -4)
📝 packages/oauth-provider/src/types/oauth.ts (+7 -0)
📝 packages/oauth-provider/src/types/zod.ts (+1 -1)
packages/oauth-provider/src/utils/cimd.test.ts (+459 -0)
packages/oauth-provider/src/utils/cimd.ts (+250 -0)
📝 packages/oauth-provider/src/utils/index.ts (+24 -1)
packages/oauth-provider/src/utils/validate-metadata-document.ts (+440 -0)

📄 Description

Implements Client ID Metadata Documents (draft-ietf-oauth-client-id-metadata-document) for verified dynamic client discovery. This is the mechanism MCP uses for authorization servers to discover clients without prior registration.

New option: clientIdMetadataDocument

When enabled, clients can use an HTTPS URL as their client_id. The server fetches and validates the metadata document at that URL, creating a client automatically on first authorization request.

oauthProvider({
  clientIdMetadataDocument: {
    refreshRate: "60m",
    originBoundFields: ["redirect_uris", "post_logout_redirect_uris", "client_uri"],
    onClientCreated({ client, metadata, ctx }) { },
    onClientRefreshed({ client, metadata, ctx }) { },
  },
})

Configuration:

  • refreshRate: TTL before re-fetching (default "60m", accepts seconds or duration strings)
  • originBoundFields: fields whose URLs must share the client_id origin (localhost always allowed for native app flows)
  • onClientCreated / onClientRefreshed: lifecycle hooks for post-processing (trust levels, logo prefetching, change detection)

Supports token_endpoint_auth_method: "none" and "private_key_jwt" (with jwks or jwks_uri).

Security

  • SSRF protection: private/reserved IP ranges (RFC 6890), IPv4-mapped IPv6, cloud metadata endpoints; runtime-agnostic (no node:dns)
  • Spec compliance: client_id must equal fetch URL, no shared secrets, no symmetric auth methods, path required, no fragments/credentials/dot-segments
  • Origin-bound fields prevent cross-domain redirect URI injection
  • Admin-only fields (skip_consent, enable_end_session, disabled) stripped from external documents
  • Only recognized RFC 7591 fields persisted; arbitrary fields discarded
  • 5 KB response size limit, 5-second fetch timeout, no redirect following

Demo

https://github.com/user-attachments/assets/abee40ad-04a6-4ca4-915b-f3511b3fb73d

Closes #7184


🔄 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/7185 **Author:** [@dvanmali](https://github.com/dvanmali) **Created:** 1/7/2026 **Status:** 🔄 Open **Base:** `next` ← **Head:** `cimd` --- ### 📝 Commits (1) - [`2ec168b`](https://github.com/better-auth/better-auth/commit/2ec168b6e4c6f02dc7a853dc0371918af3cb55b1) feat(oauth-provider): rework CIMD to Client ID Metadata Document ### 📊 Changes **14 files changed** (+1607 additions, -9 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/oauth-provider-client-id-metadata-document.md` (+40 -0) 📝 `.cspell/auth-terms.txt` (+1 -0) 📝 `demo/nextjs/lib/auth.ts` (+1 -0) 📝 `docs/content/docs/plugins/oauth-provider.mdx` (+40 -2) ➕ `packages/oauth-provider/src/cimd.test.ts` (+276 -0) 📝 `packages/oauth-provider/src/metadata.ts` (+6 -1) 📝 `packages/oauth-provider/src/oauth.ts` (+2 -0) 📝 `packages/oauth-provider/src/types/index.ts` (+60 -4) 📝 `packages/oauth-provider/src/types/oauth.ts` (+7 -0) 📝 `packages/oauth-provider/src/types/zod.ts` (+1 -1) ➕ `packages/oauth-provider/src/utils/cimd.test.ts` (+459 -0) ➕ `packages/oauth-provider/src/utils/cimd.ts` (+250 -0) 📝 `packages/oauth-provider/src/utils/index.ts` (+24 -1) ➕ `packages/oauth-provider/src/utils/validate-metadata-document.ts` (+440 -0) </details> ### 📄 Description Implements [Client ID Metadata Documents](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) (draft-ietf-oauth-client-id-metadata-document) for verified dynamic client discovery. This is the mechanism [MCP](https://modelcontextprotocol.io/specification/draft/basic/authorization#client-id-metadata-documents-flow) uses for authorization servers to discover clients without prior registration. ## New option: `clientIdMetadataDocument` When enabled, clients can use an HTTPS URL as their `client_id`. The server fetches and validates the metadata document at that URL, creating a client automatically on first authorization request. ```ts oauthProvider({ clientIdMetadataDocument: { refreshRate: "60m", originBoundFields: ["redirect_uris", "post_logout_redirect_uris", "client_uri"], onClientCreated({ client, metadata, ctx }) { }, onClientRefreshed({ client, metadata, ctx }) { }, }, }) ``` Configuration: - `refreshRate`: TTL before re-fetching (default `"60m"`, accepts seconds or duration strings) - `originBoundFields`: fields whose URLs must share the client_id origin (localhost always allowed for native app flows) - `onClientCreated` / `onClientRefreshed`: lifecycle hooks for post-processing (trust levels, logo prefetching, change detection) Supports `token_endpoint_auth_method: "none"` and `"private_key_jwt"` (with `jwks` or `jwks_uri`). ## Security - SSRF protection: private/reserved IP ranges (RFC 6890), IPv4-mapped IPv6, cloud metadata endpoints; runtime-agnostic (no `node:dns`) - Spec compliance: `client_id` must equal fetch URL, no shared secrets, no symmetric auth methods, path required, no fragments/credentials/dot-segments - Origin-bound fields prevent cross-domain redirect URI injection - Admin-only fields (`skip_consent`, `enable_end_session`, `disabled`) stripped from external documents - Only recognized RFC 7591 fields persisted; arbitrary fields discarded - 5 KB response size limit, 5-second fetch timeout, no redirect following ## Demo https://github.com/user-attachments/assets/abee40ad-04a6-4ca4-915b-f3511b3fb73d Closes #7184 --- <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-13 09:59:45 -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#15376