feat: add OAuth 2.0 authentication for remote MCP access#104
feat: add OAuth 2.0 authentication for remote MCP access#104nix-tkobayashi wants to merge 10 commits into
Conversation
Enable per-user Backlog OAuth authentication when exposing the MCP server over a network, removing the need for shared API keys. Implements the MCP Third-Party Authorization Flow (RFC 8414, RFC 9728, RFC 7591) with PKCE (S256) support. The server acts as both an OAuth authorization server for MCP clients and an OAuth client for Backlog. OAuth is opt-in: set BACKLOG_OAUTH_CLIENT_ID to enable. Existing stdio and local HTTP modes continue to work unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address review feedback: - Generate opaque access/refresh tokens; keep Backlog tokens server-side - Validate redirect_uris (require https or http://localhost) - Accept and validate the resource parameter per MCP spec - Add client registration limit (max 1000) and periodic store cleanup - Document single-org limitation and in-memory store constraint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…h rollback - Add 90-day TTL to registered clients with eviction when MAX_CLIENTS reached - Forward Backlog authorization errors (e.g. user denial) to MCP client redirect_uri - Restore refresh token on upstream refresh failure to prevent token loss - Validate token_endpoint_auth_method against supported methods - Add resource parameter validation in token exchange - Add refresh token expiry (30-day TTL) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add URLSearchParams, fetch, setInterval, clearInterval, setTimeout to ESLint globals to fix no-undef errors in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…en exchange Apply Host header validation to all routes (not just /mcp) to prevent DNS rebinding on OAuth endpoints. Make redirect_uri required in the authorization_code token exchange per RFC 6749 §4.1.3. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@nix-tkobayashi |
|
@katayama8000 |
| }); | ||
|
|
||
| // Authorization Endpoint | ||
| app.on(['GET', 'POST'], '/authorize', async (c) => { |
There was a problem hiding this comment.
@nix-tkobayashi
POST should be acceptable here?
There was a problem hiding this comment.
Good point — POST is optional per RFC 6749 §3.1, and all known MCP clients (Claude Desktop, Cursor, etc.) use browser redirects (GET) to reach /authorize. There's no concrete use case requiring POST here, so I've changed it to app.get('/authorize', ...) to keep the surface area minimal. Fixed in 76dee52.
|
@nix-tkobayashi |
…improved readability
POST is not required by RFC 6749 or the MCP spec, and all known MCP clients (Claude Desktop, Cursor, etc.) use browser redirects (GET). Removing POST simplifies the implementation and reduces attack surface. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@katayama8000 Thanks for the review and the fixes! I addressed the comment you left and updated |
Closes #105
Summary
Add OAuth 2.0 support so the MCP server can be securely exposed over a network with per-user Backlog authentication, eliminating the need for a shared API key.
BACKLOG_OAUTH_CLIENT_IDis set — existing stdio and local HTTP modes are unchangedMotivation
When using the MCP server remotely (e.g., deployed on a cloud server), sharing a single API key is a security concern. OAuth 2.0 lets each user authenticate with their own Backlog account, providing proper access control and auditability.
New files
src/auth/backlogOAuthConfig.tssrc/auth/backlogAuthContext.tssrc/auth/tokenStore.tssrc/auth/backlogOAuthClient.tssrc/auth/oauthRoutes.tssrc/auth/bearerAuthMiddleware.ts/mcpModified files
src/httpMcpServer.tsauthInfoto transportsrc/index.tssrc/utils/backlogClientRegistry.tscreateOAuthBacklogClientRegistry()using AsyncLocalStorage tokens.env.exampleREADME.md/README.ja.mdHow to test
BACKLOG_OAUTH_CLIENT_ID,BACKLOG_OAUTH_CLIENT_SECRET,BACKLOG_DOMAIN,MCP_SERVER_BASE_URL--transport httpcurl <base>/.well-known/oauth-authorization-serverPOST /registerAuthorization: Bearer <token>on/mcpTest plan
🤖 Generated with Claude Code