feat: OAuth 2.0 gateway for Claude.ai web connectivity#31
Open
fjprobos wants to merge 2 commits intoCognitionAI:mainfrom
Open
feat: OAuth 2.0 gateway for Claude.ai web connectivity#31fjprobos wants to merge 2 commits intoCognitionAI:mainfrom
fjprobos wants to merge 2 commits intoCognitionAI:mainfrom
Conversation
Adds an OAuth 2.0 Authorization Code + PKCE gateway so that web-based MCP clients (Claude.ai, Claude Code web) can connect without pre-sharing credentials via request headers. ## Why this is needed The HTTP Stream transport (PR CognitionAI#30) requires clients to pass Metabase credentials as request headers. Web clients like Claude.ai do not support custom headers — they require a standard OAuth 2.0 flow. This gateway bridges that gap. ## How it works 1. Client discovers the gateway via `/.well-known/oauth-authorization-server` 2. User is redirected to `/oauth/authorize` — an HTML form asking for Metabase URL + API key (or username/password) 3. On submit, server stores credentials under a short-lived auth code and redirects back to the client 4. Client exchanges the code at `/oauth/token` for a signed JWT 5. All `/mcp` requests carry `Authorization: Bearer <JWT>` 6. Gateway validates the JWT, injects `x-metabase-*` headers, and proxies to the upstream HTTP Stream server ## Security - Credentials are never logged (only session prefix + status) - Client registration details logged only at LOG_LEVEL=debug - Auth codes are single-use with a 10-minute TTL - PKCE (S256) support for public clients - JWT signed with configurable JWT_SECRET ## Tests (28 passing) - OAuth discovery endpoints - Dynamic client registration (RFC 7591) - Authorization endpoint: form rendering, XSS escaping, validation - Token endpoint: api-key flow, username/password flow, single-use codes - PKCE: correct verifier, wrong verifier, missing verifier - MCP proxy: 401 without token, 401 with invalid token Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Intercept notifications/initialized at gateway level and return 202 instead of proxying (upstream returns 400 in stateless mode) - Refactor proxy into doProxy(attempt) with one retry on 400 for non-initialize methods (60ms delay, handles stateless race condition) - Add @types/express devDependency - Normalize log level strings to lowercase Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Why this is needed
The HTTP Stream transport (PR #30) requires clients to pass Metabase credentials as request headers. Web-based clients like Claude.ai and Claude Code web do not support custom headers — they require a standard OAuth 2.0 flow. This gateway bridges that gap.
--headerflagsHow it works
/.well-known/oauth-authorization-server/oauth/authorize— an HTML form asking for Metabase URL + API key (or username/password)/oauth/tokenfor a signed JWT (10-min TTL on code, configurable token expiry)/mcprequests carryAuthorization: Bearer <JWT>x-metabase-*headers, and proxies to the upstream HTTP Stream serverUsage
Then add to Claude.ai / Claude Code web:
The user will be prompted with a login form on first connection.
Security
LOG_LEVEL=debugJWT_SECRETEnvironment variables
GATEWAY_URLhttp://localhost:8080GATEWAY_PORT8080MCP_UPSTREAMhttp://localhost:8011JWT_SECRETTOKEN_EXPIRY8h1h,24h)LOG_LEVELinfodebugfor verbose loggingTests (28 passing)
/.well-known/oauth-authorization-server,/.well-known/openid-configuration)Test plan
node dist/oauth-gateway.jsstarts on configured portGET /.well-known/oauth-authorization-serverreturns discovery documentnpm test— 28 tests pass🤖 Generated with Claude Code