Skip to content

fix: support chatgpt mcp connections with private_key_jwt client authentication - #926

Merged
leynier merged 1 commit into
mainfrom
fix-chatgpt-public-mcp-client
Oct 10, 2026
Merged

leynier merged 1 commit into
mainfrom
fix-chatgpt-public-mcp-client

Conversation

@leynier

@leynier leynier commented Oct 10, 2026

Copy link
Copy Markdown
Owner

Summary

Each MCP client now has exactly one token-endpoint authentication method, none or private_key_jwt, and /oauth/token enforces that method. private_key_jwt clients must present a single-use JWT bearer assertion, verified against their HTTPS JWKS, before an authorization code is consumed. Public clients that send an assertion are rejected. ChatGPT Client ID Metadata Documents that declare private_key_jwt with an HTTPS jwks_uri are stored as confidential clients.

Screenshots

No visual change.

Testing

  • dart format --set-exit-if-changed lib test integration_test tool
  • flutter analyze
  • flutter test --coverage --exclude-tags golden
  • dart run tool/quality/coverage_report.dart --input coverage/lcov.info --min-lines 100 --worst 25
  • Golden tests, if UI changed: flutter test --tags golden
  • Desktop E2E, if app-shell flow changed: flutter test integration_test -d macos
  • Relevant desktop build: flutter build macos, flutter build windows, or flutter build linux
  • Landing checks, if applicable: cd landing && bun run check
  • Added or updated tests that would catch regressions, or explained why tests were not needed

Flutter, golden, desktop E2E, desktop build, and landing checks do not apply. This change is in the cloud authorization server.

Added unit coverage for assertion claim checks, algorithm and key rejection, public-client refusal, form credential parsing, method selection, and the ChatGPT metadata document. Added cloud/tests/contracts/mcp_client_auth_contract.rs for the token-endpoint contract. Schema migration 0023 is included in the required startup set.

AI Review Report

No separate AI review run is recorded for this branch.

Cross-platform shortcut, label, path, shell, terminal, release, and updater behavior does not apply. The change is server-side OAuth client authentication.

Security Audit

Reviewed token-endpoint client authentication.

  • A client is bound to one method. The token endpoint never accepts both none and private_key_jwt.
  • Assertions require the JWT bearer type, RS256, PS256, or ES256, issuer and subject equal to the client id, and an audience of the issuer or the token endpoint. HMAC and alg=none are rejected. A key that names an algorithm may verify only that algorithm.
  • jti values are stored as hashes and inserted with ON CONFLICT DO NOTHING. A replay returns invalid_client. Expired assertion rows are deleted by retention cleanup.
  • The client is authenticated before an authorization code is consumed.
  • JWKS URIs must be HTTPS, without userinfo or a fragment. Fetches are cached, single-flight, and rate-limited, including failed attempts, so unknown kid values cannot force a fetch per request.
  • The assertion subject is used only to locate the client. Signature verification happens afterward. Errors do not echo the assertion.
  • OAuth secrets, signing keys, and bearer tokens are not logged. Rejection logs record the verifier error kind.

Notes

Migration 0023_mcp_client_authentication.sql adds token_endpoint_auth_method, jwks_uri, and token_endpoint_auth_signing_alg on mcp_clients, plus mcp_client_assertions for jti replay. Existing rows default to none.

Clients that declare an unsupported method fall back to none only when they also list none. Otherwise registration is refused.

The branch also moves the reference_projects/code_forge and reference_projects/jean submodule pointers.

- Require each MCP client to use exactly one token method: none or private_key_jwt.
- Verify private_key_jwt assertions against the client JWKS and reject replayed jti values.
- Authenticate the client before an authorization code is consumed.
@leynier leynier changed the title enforce-mcp-client-authentication fix: support chatgpt mcp connections with private_key_jwt client authentication Oct 10, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Reviewed the cloud OAuth token-endpoint client authentication change at ce7fd8e7, including assertion verification, JWKS fetching, grant ordering, migration 0023, and the ChatGPT Client ID Metadata Document path.

  • Method binding: Each MCP client stores one token-endpoint method, none or private_key_jwt, and /oauth/token enforces that method instead of accepting either.
  • Assertion checks: A private_key_jwt client must present a single-use RFC 7523 JWT, verified against its HTTPS JWKS, before an authorization code is consumed or treated as a replay.
  • ChatGPT CIMD: A metadata document that declares private_key_jwt with an HTTPS jwks_uri is stored as confidential. An unsupported declared method falls back to public only when none is also listed.
  • Replay and fetch limits: jti values are stored as hashes with ON CONFLICT DO NOTHING. JWKS fetches reuse the metadata document fetcher's address and redirect guards, with a per-URI cache and rate limit.
  • Reference pins: This commit also moves the reference_projects/code_forge and reference_projects/jean submodule pointers. Those pins are not part of the cloud runtime.

Pullfrog  | View workflow run | Using grok-4.7 | 𝕏

@leynier
leynier merged commit b890ab4 into main Oct 10, 2026
21 checks passed
@leynier
leynier deleted the fix-chatgpt-public-mcp-client branch October 10, 2026 04:15
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