Skip to content

Conversation

@roomote
Copy link
Contributor

@roomote roomote bot commented Aug 21, 2025

Summary

This PR adds native OAuth authentication support for streamable-http MCP servers, addressing Issue #7296. Previously, users had to rely on the mcp-remote package as a workaround to handle OAuth flows. This implementation enables Roo to handle OAuth authentication directly.

Changes

Core Implementation

  • OAuthHandler: New service to manage OAuth authentication flows

    • Handles OAuth 2.0 authorization code flow with PKCE
    • Manages token storage securely in VS Code's global storage
    • Supports token refresh and re-authentication
    • Opens browser for user authorization
    • Runs local callback server to receive authorization codes
  • OAuthStreamableHTTPClientTransport: Wrapper for StreamableHTTPClientTransport

    • Intercepts HTTP requests to add OAuth headers
    • Automatically handles token refresh on 401 responses
    • Manages authentication state transparently
  • McpHub Updates: Enhanced to support OAuth configuration

    • Extended server schema to include OAuth settings
    • Detects OAuth configuration and uses appropriate transport
    • Maintains backward compatibility with non-OAuth servers

Configuration

MCP servers can now be configured with OAuth settings:

{
  "mcpServers": {
    "azure-secured-server": {
      "type": "streamable-http",
      "url": "http://localhost:8005/mcp",
      "oauth": {
        "clientId": "your-client-id",
        "clientSecret": "your-client-secret",
        "authorizationUrl": "https://login.microsoftonline.com/tenant/oauth2/v2.0/authorize",
        "tokenUrl": "https://login.microsoftonline.com/tenant/oauth2/v2.0/token",
        "scopes": ["User.Read", "email", "openid", "profile"]
      }
    }
  }
}

Testing

  • Added comprehensive unit tests for OAuthHandler
  • Tests cover token storage, retrieval, refresh, and authentication flows
  • TypeScript compilation passes without errors
  • ESLint checks pass

Fixes

Fixes #7296

Notes

  • OAuth tokens are stored securely in VS Code's global storage
  • The implementation supports standard OAuth 2.0 flows with PKCE for enhanced security
  • Backward compatibility is maintained - servers without OAuth configuration work as before
  • The local callback server runs on port 3000 to receive authorization codes

Important

Adds OAuth authentication support for streamable-http MCP servers, including new classes for handling OAuth flows and updates to existing server configurations.

  • Behavior:
    • Adds OAuthHandler to manage OAuth 2.0 flows, token storage, and refresh in OAuthHandler.ts.
    • Introduces OAuthStreamableHTTPClientTransport to wrap StreamableHTTPClientTransport and handle OAuth headers in OAuthStreamableHTTPClientTransport.ts.
    • Updates McpHub.ts to support OAuth configuration, maintaining backward compatibility with non-OAuth servers.
  • Configuration:
    • MCP servers can be configured with OAuth settings, including clientId, clientSecret, authorizationUrl, tokenUrl, and scopes.
  • Testing:
    • Adds unit tests for OAuthHandler in __tests__/OAuthHandler.spec.ts, covering token storage, retrieval, refresh, and authentication flows.
  • Misc:

This description was created by Ellipsis for ce1000b. You can customize this summary. It will automatically update as commits are pushed.

- Implement OAuthHandler to manage OAuth flows and token storage
- Create OAuthStreamableHTTPClientTransport wrapper for OAuth-enabled connections
- Add OAuth configuration support to MCP server schema
- Store OAuth tokens securely in VS Code global storage
- Support token refresh and re-authentication on 401 responses
- Add comprehensive tests for OAuth functionality

Fixes #7296
@roomote roomote bot requested review from cte, jr and mrubens as code owners August 21, 2025 18:52
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Aug 21, 2025
Copy link
Contributor Author

@roomote roomote bot left a comment

Choose a reason for hiding this comment

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

I wrote OAuth code that modifies global state. Even I'm questioning my life choices.


this.transport.start = async () => {
// Replace fetch
globalThis.fetch = oauthFetch as typeof fetch
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a critical issue - modifying globalThis.fetch could have unintended side effects on other parts of the application. Could we consider using a more isolated approach, perhaps by:

  1. Creating a custom fetch instance specifically for this transport
  2. Using the transport's own HTTP client instead of modifying the global one
  3. Implementing a proxy pattern that doesn't affect the global scope?

The current approach could break other extensions or VS Code features that rely on the standard fetch behavior.

}
})

this.server.listen(3000, "localhost", () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The hardcoded port 3000 could cause conflicts with development servers or be blocked by corporate firewalls. Could we make this configurable or implement port detection?

try {
const data = Object.fromEntries(this.tokenStorage)
await fs.mkdir(path.dirname(this.storageFilePath), { recursive: true })
await fs.writeFile(this.storageFilePath, JSON.stringify(data, null, 2))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Storing OAuth tokens in plain JSON is a security concern. VS Code provides a SecretStorage API specifically for sensitive data. Could we refactor this to use:

oauth-

This would provide encrypted storage and better security for user credentials.

private shouldInterceptRequest(url: string): boolean {
// This is a simplified check - you might want to make this more sophisticated
// For now, we'll intercept all requests while this transport is active
return true
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this intentional? This method always returns true, meaning we intercept ALL fetch requests while the transport is active, not just those to the MCP server. This could impact performance and cause unexpected behavior. Should we check if the URL matches our MCP server URL?

/**
* Start the OAuth flow for a server
*/
public async authenticate(serverName: string, config: OAuthConfig): Promise<OAuthTokens | null> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If OAuth authentication fails, there's no way for users to retry without restarting VS Code. Could we add a command or method to clear failed authentication state and retry?

}),
}))

describe("OAuthHandler", () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The test suite is missing coverage for critical error scenarios:

  • What happens when the OAuth callback server fails to start?
  • How does the system handle token refresh failures?
  • What about testing the global fetch modification in OAuthStreamableHTTPClientTransport?

Adding these tests would help ensure the robustness of the OAuth implementation.

vscode.window.showErrorMessage(`OAuth authentication timeout for ${serverName}`)
}
},
5 * 60 * 1000,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A 5-minute timeout might be too long from a UX perspective. Users might think the process is stuck. Could we consider:

  1. Reducing to 2-3 minutes
  2. Adding a progress notification that updates periodically
  3. Allowing users to cancel the authentication flow?

// OAuth configuration (optional)
oauth: z
.object({
clientId: z.string(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The OAuth configuration is added to the schema but there's no validation of the OAuth URLs or required fields before use. Could we add validation to ensure the URLs are valid and all required OAuth fields are present when oauth is configured?

@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Aug 21, 2025
@hsuyuming
Copy link

Could you follow the MCP specification (https://modelcontextprotocol.io/specification/draft/basic/authorization)), which involves sending a request to /.well-known/oauth-protected-resource to obtain the resource metadata and authorization server URL, and then accessing the authorization server's .well-known/oauth-authorization-server to start the OAuth process?

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

Labels

enhancement New feature or request Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Roo does not initiate the OAuth flow if the remote MCP server implements Authorization.

5 participants