mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-06 19:58:43 +02:00
feat: implement MCP OAuth 2.1 flow, token storage, and injection
- Create mcpTokenStore: in-memory token storage keyed by normalized server URL with automatic expiry checking - Create mcpOAuthService: full OAuth 2.1 + PKCE flow implementation - Protected Resource Metadata discovery (RFC 9728) - Authorization Server Metadata fetch (RFC 8414) - Dynamic Client Registration (RFC 7591) when no static client ID - PKCE S256 code challenge generation - Local HTTP callback server for auth code receipt - Browser-based consent via Electron shell.openExternal - Authorization code to token exchange - Add startSessionMcpAuth IPC channel and handler to trigger OAuth flow - Inject stored OAuth tokens as Authorization headers in buildRunTurnToolingConfig - Update McpAuthBanner with 'Authenticate in browser' button and loading state - Add tests for token store (7 tests) and token injection (3 tests) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -117,4 +117,59 @@ describe('session tooling config helpers', () => {
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test('injects OAuth token as Authorization header for remote MCP servers', () => {
|
||||
const tokenLookup = (url: string) =>
|
||||
url === 'https://example.com/mcp' ? 'oauth-access-token' : undefined;
|
||||
|
||||
const config = buildRunTurnToolingConfig(
|
||||
TOOLING,
|
||||
{ enabledMcpServerIds: ['mcp-remote'], enabledLspProfileIds: [] },
|
||||
tokenLookup,
|
||||
);
|
||||
|
||||
expect(config?.mcpServers[0]).toMatchObject({
|
||||
id: 'mcp-remote',
|
||||
headers: { Authorization: 'Bearer oauth-access-token' },
|
||||
});
|
||||
});
|
||||
|
||||
test('preserves existing headers when injecting OAuth token', () => {
|
||||
const toolingWithHeaders: WorkspaceToolingSettings = {
|
||||
...TOOLING,
|
||||
mcpServers: [
|
||||
{
|
||||
id: 'mcp-custom',
|
||||
name: 'Custom MCP',
|
||||
transport: 'http',
|
||||
url: 'https://custom.example.com/mcp',
|
||||
headers: { 'X-Custom': 'value' },
|
||||
tools: [],
|
||||
createdAt: TIMESTAMP,
|
||||
updatedAt: TIMESTAMP,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const config = buildRunTurnToolingConfig(
|
||||
toolingWithHeaders,
|
||||
{ enabledMcpServerIds: ['mcp-custom'], enabledLspProfileIds: [] },
|
||||
() => 'my-token',
|
||||
);
|
||||
|
||||
expect(config?.mcpServers[0].headers).toEqual({
|
||||
'X-Custom': 'value',
|
||||
Authorization: 'Bearer my-token',
|
||||
});
|
||||
});
|
||||
|
||||
test('does not inject Authorization header when no token is available', () => {
|
||||
const config = buildRunTurnToolingConfig(
|
||||
TOOLING,
|
||||
{ enabledMcpServerIds: ['mcp-remote'], enabledLspProfileIds: [] },
|
||||
() => undefined,
|
||||
);
|
||||
|
||||
expect(config?.mcpServers[0].headers).toEqual({ Authorization: 'Bearer token' });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user