[go: up one dir, main page]

Skip to content

Fix PKCE validation blocking MCP token refresh

What does this MR do and why?

  • Fixes token refresh for dynamic OAuth applications by skipping refresh token grants from PKCE validation.
  • Fixes MCP client authorization by returning only the mcp scope in the MCP-specific OAuth discovery endpoint.

PKCE is only required for authorization code exchange per RFC 7636, not for refresh token grants. This allows MCP clients to automatically refresh expired tokens without re-authentication.

When MCP clients (like Cursor) try to refresh expired access tokens, the request fails with:

PKCE code_verifier is required for dynamic OAuth applications

This prevents automatic token refresh, breaking the UX when tokens expire.

Note: I still have issues connecting with Claude Desktop and Claude Code, but that's probably because of some specific configuration options. Looking into it see explanation below.

For the MCP-specific OAuth discovery endpoint fix, check the discussion in #570482 (comment 2840378558)

References

https://datatracker.ietf.org/doc/html/rfc8252#section-8.1

Related to #577575

How to set up and validate locally

Prerequisites

  • GDK running locally
  • Cursor installed
  • MCP feature flag enabled

Steps

  1. Clean up existing OAuth apps:

    gdk rails c
    # Delete all dynamic MCP applications and tokens
    apps = Doorkeeper::Application.where(dynamic: true, scopes: 'mcp')
    apps.each do |app|
      OauthAccessToken.where(application_id: app.id).each(&:revoke)
      app.destroy
    end
  2. Clean up Cursor's OAuth cache:

    # Close Cursor first
    # For macOS
    sqlite3 ~/Library/Application\ Support/Cursor/User/globalStorage/state.vscdb \
      "DELETE FROM ItemTable WHERE key LIKE '%mcp_client_information%' OR key LIKE '%mcp_tokens%';"
  3. Configure Cursor with MCP server:

    Add to Cursor settings (JSON):

    {
      "mcpServers": {
        "GitLab-GDK-http": {
          "type": "http",
          "url": "https://gdk.test:3443/api/v4/mcp"
        }
      }
    }
  4. Initial authentication (should work):

    • Open Cursor
    • Go to MCP settings
    • Click "Connect" on GitLab-GDK-http
    • Browser opens for OAuth authorization
    • Approve the application
    • Connection successful
  5. Expire the token to trigger refresh:

    # In Rails console
    app = Doorkeeper::Application.where(dynamic: true).order(created_at: :desc).first
    token = OauthAccessToken.where(application_id: app.id).last
    
    # Make token expire 3 hours ago (the time actually doesn't really matter as long as it's in the past)
    token.update(created_at: 3.hours.ago)
  6. Trigger token refresh (bug appears here):

    • Restart Cursor, the connection won't be successful
    • Without the fix logs show: Fails with "PKCE code_verifier is required"
    • With the fix: Token automatically refreshes, connection works

Expected Logs (Bug)

Without fix:

Started POST "/oauth/token" for 127.0.0.1
Parameters: {"grant_type"=>"refresh_token", "refresh_token"=>"[FILTERED]", "client_id"=>"..."}
Completed 400 Bad Request
{"error":"invalid_request","error_description":"PKCE code_verifier is required for dynamic OAuth applications"}

With fix:

Started POST "/oauth/token" for 127.0.0.1
Parameters: {"grant_type"=>"refresh_token", "refresh_token"=>"[FILTERED]", "client_id"=>"..."}
Completed 200 OK

Claude Code + GDK

It seems like Claude Code cannot connect to HTTP MCP servers running locally. I've added GDK MCP server but it always fails to connect:

GitLab-GDK MCP Server 
                                                                                                                                                                                                                                                                                                     
Status: ✘ failed                                                                                                                                                                                                                                                                                                           
URL: https://gdk.test:3443/api/v4/mcp   

Here is quick chat with Claude Code about it

Screenshot_2025-10-23_at_16.05.59

The same issue with Gemini CLI, but it doesn't seem aware about it:

Screenshot_2025-10-23_at_16.17.02

Claude Desktop

Similar issues with Claude Desktop. If I change config in claude_desktop_config.json and specify HTTP MCP, it fails

Screenshot_2025-10-22_at_16.16.22

I think HTTP MCP servers considered remote and should be configured differently https://modelcontextprotocol.io/docs/develop/connect-remote-servers#connecting-to-a-remote-mcp-server

However, the documentation is either outdated, or I don't have access to that feature

Documentation My settings
Screenshot_2025-10-23_at_16.25.34 Screenshot_2025-10-23_at_16.26.06

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Vitali Tatarintev

Merge request reports

Loading