-
Notifications
You must be signed in to change notification settings - Fork 2k
Add CIMD (Client ID Metadata Document) support for OAuth #2871
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
35fd196
Add CIMD module with document model, fetcher, and trust policy
jlowin 8fd852b
Integrate CIMD support into OAuthProxy
jlowin f0a0f20
Enhance consent screen with CIMD domain verification badges
jlowin 49a1216
Add CIMD CLI commands for document creation and validation
jlowin 8ce6bae
Add unit tests for CIMD functionality
jlowin f1f493e
Fix CIMD implementation issues and test failures
jlowin f5f602d
Refactor OAuth client API to eliminate redundant URL specification
jlowin 46b2dc7
Simplify CIMD implementation and add comprehensive tests
jlowin 01b213d
Add comprehensive CIMD documentation
jlowin 6718313
Harden CIMD SSRF protection and remove trust policy system
jlowin af22764
ruff
jlowin b979822
Fix code quality issues in CIMD implementation
jlowin 192aa6d
Harden CIMD implementation with additional validation
jlowin af1a099
Fix CIMD security implementation bugs
jlowin 2094c88
Mark CIMD support as beta feature
jlowin 7f850a5
WIP: CIMD security hardening and redirect validation
jlowin ab38680
Fix rebase conflicts with oauth_proxy package restructuring
jlowin 170c410
Revert OAuth client API refactor to match main
jlowin 6a73423
Integrate CIMD support into OAuth proxy
jlowin c292694
Add CIMD client support, documentation, and comprehensive tests
jlowin 8c0c57d
Add CIMD to v3 feature tracking
jlowin d807448
Address review feedback: JWKS caching, SSRF scope, redirect validation
jlowin 52b716f
Collapse CIMDFetcher onto ssrf_safe_fetch, remove redundant SSRF tests
jlowin 22f86b1
Revert loq cimd.py limit to 683
jlowin b9b9bf7
Restore loq.toml to main defaults
jlowin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| --- | ||
| title: CIMD Authentication | ||
| sidebarTitle: CIMD | ||
| description: Use Client ID Metadata Documents for verifiable, domain-based client identity. | ||
| icon: id-badge | ||
| --- | ||
|
|
||
| import { VersionBadge } from "/snippets/version-badge.mdx" | ||
|
|
||
| <VersionBadge version="3.0.0" /> | ||
|
|
||
| <Tip> | ||
| CIMD authentication is only relevant for HTTP-based transports and requires a server that advertises CIMD support. | ||
| </Tip> | ||
|
|
||
| With standard OAuth, your client registers dynamically with every server it connects to, receiving a fresh `client_id` each time. This works, but the server has no way to verify *who* your client actually is — any client can claim any name during registration. | ||
|
|
||
| CIMD (Client ID Metadata Documents) flips this around. You host a small JSON document at an HTTPS URL you control, and that URL becomes your `client_id`. When your client connects to a server, the server fetches your metadata document and can verify your identity through your domain ownership. Users see a verified domain badge in the consent screen instead of an unverified client name. | ||
|
|
||
| ## Client Usage | ||
|
|
||
| Pass your CIMD document URL to the `client_metadata_url` parameter of `OAuth`: | ||
|
|
||
| ```python | ||
| from fastmcp import Client | ||
| from fastmcp.client.auth import OAuth | ||
|
|
||
| async with Client( | ||
| "https://mcp-server.example.com/mcp", | ||
| auth=OAuth( | ||
| client_metadata_url="https://myapp.example.com/oauth/client.json", | ||
| ), | ||
| ) as client: | ||
| await client.ping() | ||
| ``` | ||
|
|
||
| When the server supports CIMD, the client uses your metadata URL as its `client_id` instead of performing Dynamic Client Registration. The server fetches your document, validates it, and proceeds with the standard OAuth authorization flow. | ||
|
|
||
| <Note> | ||
| You don't need to pass `mcp_url` when using `OAuth` with `Client(auth=...)` — the transport provides the server URL automatically. | ||
| </Note> | ||
|
|
||
| ## Creating a CIMD Document | ||
|
|
||
| A CIMD document is a JSON file that describes your client. The most important field is `client_id`, which must exactly match the URL where you host the document. | ||
|
|
||
| Use the FastMCP CLI to generate one: | ||
|
|
||
| ```bash | ||
| fastmcp auth cimd create \ | ||
| --name "My Application" \ | ||
| --redirect-uri "http://localhost:*/callback" \ | ||
| --client-id "https://myapp.example.com/oauth/client.json" | ||
| ``` | ||
|
|
||
| This produces: | ||
|
|
||
| ```json | ||
| { | ||
| "client_id": "https://myapp.example.com/oauth/client.json", | ||
| "client_name": "My Application", | ||
| "redirect_uris": ["http://localhost:*/callback"], | ||
| "token_endpoint_auth_method": "none", | ||
| "grant_types": ["authorization_code"], | ||
| "response_types": ["code"] | ||
| } | ||
| ``` | ||
|
|
||
| If you omit `--client-id`, the CLI generates a placeholder value and reminds you to update it before hosting. | ||
|
|
||
| ### CLI Options | ||
|
|
||
| The `create` command accepts these flags: | ||
|
|
||
| | Flag | Description | | ||
| |------|-------------| | ||
| | `--name` | Human-readable client name (required) | | ||
| | `--redirect-uri`, `-r` | Allowed redirect URIs — can be specified multiple times (required) | | ||
| | `--client-id` | The URL where you'll host this document (sets `client_id` directly) | | ||
| | `--output`, `-o` | Write to a file instead of stdout | | ||
| | `--scope` | Space-separated list of scopes the client may request | | ||
| | `--client-uri` | URL of the client's home page | | ||
| | `--logo-uri` | URL of the client's logo image | | ||
| | `--no-pretty` | Output compact JSON | | ||
|
|
||
| ### Redirect URIs | ||
|
|
||
| The `redirect_uris` field supports wildcard port matching for localhost. The pattern `http://localhost:*/callback` matches any port, which is useful for development clients that bind to random available ports (which is what FastMCP's `OAuth` helper does by default). | ||
|
|
||
| ## Hosting Requirements | ||
|
|
||
| CIMD documents must be hosted at a publicly accessible HTTPS URL with a non-root path: | ||
|
|
||
| - **HTTPS required** — HTTP URLs are rejected for security | ||
| - **Non-root path** — The URL must have a path component (e.g., `/oauth/client.json`, not just `/`) | ||
| - **Public accessibility** — The server must be able to fetch the document over the internet | ||
| - **Matching `client_id`** — The `client_id` field in the document must exactly match the hosting URL | ||
|
|
||
| Common hosting options include static file hosting services like GitHub Pages, Cloudflare Pages, Vercel, or S3 — anywhere you can serve a JSON file over HTTPS. | ||
|
|
||
| ## Validating Your Document | ||
|
|
||
| Before deploying, verify your hosted document passes validation: | ||
|
|
||
| ```bash | ||
| fastmcp auth cimd validate https://myapp.example.com/oauth/client.json | ||
| ``` | ||
|
|
||
| The validator fetches the document and checks that: | ||
| - The URL is valid (HTTPS, non-root path) | ||
| - The document is well-formed JSON conforming to the CIMD schema | ||
| - The `client_id` in the document matches the URL it was fetched from | ||
|
|
||
| ## How It Works | ||
|
|
||
| When your client connects to a CIMD-enabled server, the flow works like this: | ||
|
|
||
| <Steps> | ||
| <Step title="Client Presents Metadata URL"> | ||
| Your client sends its `client_metadata_url` as the `client_id` in the OAuth authorization request. | ||
| </Step> | ||
| <Step title="Server Recognizes CIMD URL"> | ||
| The server sees that the `client_id` is an HTTPS URL with a path — the signature of a CIMD client — and skips Dynamic Client Registration. | ||
| </Step> | ||
| <Step title="Server Fetches and Validates"> | ||
| The server fetches your JSON document from the URL, validates that `client_id` matches the URL, and extracts your client metadata (name, redirect URIs, scopes). | ||
| </Step> | ||
| <Step title="Authorization Proceeds"> | ||
| The standard OAuth flow continues: browser opens for user consent, authorization code exchange, token issuance. The consent screen shows your verified domain. | ||
| </Step> | ||
| </Steps> | ||
|
|
||
| The server caches your CIMD document according to HTTP cache headers, so subsequent requests don't require re-fetching. | ||
|
|
||
| ## Server Configuration | ||
|
|
||
| CIMD is a server-side feature that your MCP server must support. FastMCP's OAuth proxy providers (GitHub, Google, Auth0, etc.) support CIMD by default. See the [OAuth Proxy CIMD documentation](/servers/auth/oauth-proxy#cimd-support) for server-side configuration, including private key JWT authentication and security details. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -198,6 +198,7 @@ | |
| "icon": "key", | ||
| "pages": [ | ||
| "clients/auth/oauth", | ||
| "clients/auth/cimd", | ||
| "clients/auth/bearer" | ||
| ] | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: jlowin/fastmcp
Length of output: 7631
🏁 Script executed:
Repository: jlowin/fastmcp
Length of output: 2612
🏁 Script executed:
Repository: jlowin/fastmcp
Length of output: 2011
🏁 Script executed:
Repository: jlowin/fastmcp
Length of output: 40
🏁 Script executed:
Repository: jlowin/fastmcp
Length of output: 40
Update OAuth parameter documentation to match the actual constructor signature.
The parameter list is missing
mcp_url(a required first parameter shown in the example), and incorrectly includesclient_metadata_urlwhich does not exist in the actualOAuth.__init__signature. The factory typeMcpHttpClientFactoryis correctly cased. Update lines 52–58 to documentmcp_urlas the first required parameter and removeclient_metadata_urlfrom the list.🧰 Tools
🪛 LanguageTool
[style] ~52-~52: To form a complete sentence, be sure to include a subject or ‘there’.
Context: ...]`, optional): OAuth scopes to request. Can be space-separated string or list of st...
(MISSING_IT_THERE)