Skip to content

fix(oauth): dynamic client registration fails with jwt-bearer grant type #1531

Description

@edelauna

Problem

Zoo Code's MCP OAuth client includes urn:ietf:params:oauth:grant-type:jwt-bearer in the grant_types array during dynamic client registration (RFC 7591). PostHog's registration endpoint rejects this grant type even though it appears in their server metadata.

The error returned:

{"error":"invalid_client_metadata","error_description":"{'grant_types': {2: [ErrorDetail(string='\"urn:ietf:params:oauth:grant-type:jwt-bearer\" is not a valid choice.', code='invalid_choice')]}}"}

Root cause

Zoo Code reads grant_types_supported from the authorization server's /.well-known/oauth-authorization-server metadata and includes all of them in the registration request. PostHog advertises jwt-bearer in their metadata but their registration endpoint only accepts authorization_code and refresh_token for dynamically registered clients. jwt-bearer is reserved for a separate agent identity flow (ID-JAG) that Zoo Code does not use.

Reproduction

# Fails — jwt-bearer in grant_types
curl -X POST https://oauth.posthog.com/oauth/register/   -H "Content-Type: application/json"   -d '{
    "client_name": "zoo-code",
    "redirect_uris": ["http://localhost:12345/callback"],
    "grant_types": ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:jwt-bearer"],
    "response_types": ["code"],
    "token_endpoint_auth_method": "none"
  }'

# Succeeds — jwt-bearer removed
curl -X POST https://oauth.posthog.com/oauth/register/   -H "Content-Type: application/json"   -d '{
    "client_name": "zoo-code",
    "redirect_uris": ["http://localhost:12345/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"],
    "token_endpoint_auth_method": "none"
  }'

Fix

When building the grant_types array for a dynamic client registration request, only include grant types the client actually uses. Filter out urn:ietf:params:oauth:grant-type:jwt-bearer (and any other non-standard types from grant_types_supported) before sending the registration payload.

The safe allowlist for dynamic registration is:

  • authorization_code
  • refresh_token (only if the client will use refresh tokens)

TDD approach

Write tests first, then implement the fix.

Test 1 — registration payload does not include jwt-bearer
Given an authorization server that advertises jwt-bearer in grant_types_supported,
when Zoo Code builds the dynamic client registration request,
then the grant_types array must not contain urn:ietf:params:oauth:grant-type:jwt-bearer.

Test 2 — registration succeeds against a server that rejects jwt-bearer
Mock a registration endpoint that returns invalid_client_metadata when jwt-bearer is present and 200 otherwise.
Confirm Zoo Code's registration call succeeds.

Test 3 — non-standard grant types are filtered generally
Given a server that advertises an unknown grant type urn:example:grant-type:foo,
the registration payload must only contain types from the safe allowlist.

Server metadata reference

GET https://oauth.posthog.com/.well-known/oauth-authorization-server
grant_types_supported: ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:jwt-bearer"]

The registration endpoint at https://oauth.posthog.com/oauth/register/ silently strips jwt-bearer on some paths and returns invalid_client_metadata on others, so the fix must be client-side.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions