mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
This commit is contained in:
committed by
GitHub
parent
2bb7aaecd0
commit
be03e0619f
@@ -2056,6 +2056,90 @@ describe('connectToMcpServer with OAuth', () => {
|
||||
capturedTransport._requestInit?.headers?.['Authorization'];
|
||||
expect(authHeader).toBe('Bearer test-access-token-from-discovery');
|
||||
});
|
||||
|
||||
it('should use discoverOAuthFromWWWAuthenticate when it succeeds and skip discoverOAuthConfig', async () => {
|
||||
const serverUrl = 'http://test-server.com/mcp';
|
||||
const authUrl = 'http://auth.example.com/auth';
|
||||
const tokenUrl = 'http://auth.example.com/token';
|
||||
const wwwAuthHeader = `Bearer realm="test", resource_metadata="http://test-server.com/.well-known/oauth-protected-resource"`;
|
||||
|
||||
vi.mocked(mockedClient.connect).mockRejectedValueOnce(
|
||||
new StreamableHTTPError(
|
||||
401,
|
||||
`Unauthorized\nwww-authenticate: ${wwwAuthHeader}`,
|
||||
),
|
||||
);
|
||||
|
||||
vi.mocked(OAuthUtils.discoverOAuthFromWWWAuthenticate).mockResolvedValue({
|
||||
authorizationUrl: authUrl,
|
||||
tokenUrl,
|
||||
scopes: ['read'],
|
||||
});
|
||||
|
||||
vi.mocked(mockedClient.connect).mockResolvedValueOnce(undefined);
|
||||
|
||||
const client = await connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ httpUrl: serverUrl, oauth: { enabled: true } },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(client).toBe(mockedClient);
|
||||
expect(OAuthUtils.discoverOAuthFromWWWAuthenticate).toHaveBeenCalledWith(
|
||||
wwwAuthHeader,
|
||||
serverUrl,
|
||||
);
|
||||
expect(OAuthUtils.discoverOAuthConfig).not.toHaveBeenCalled();
|
||||
expect(mockAuthProvider.authenticate).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('should fall back to extractBaseUrl + discoverOAuthConfig when discoverOAuthFromWWWAuthenticate returns null', async () => {
|
||||
const serverUrl = 'http://test-server.com/mcp';
|
||||
const baseUrl = 'http://test-server.com';
|
||||
const authUrl = 'http://auth.example.com/auth';
|
||||
const tokenUrl = 'http://auth.example.com/token';
|
||||
const wwwAuthHeader = `Bearer realm="test"`;
|
||||
|
||||
vi.mocked(mockedClient.connect).mockRejectedValueOnce(
|
||||
new StreamableHTTPError(
|
||||
401,
|
||||
`Unauthorized\nwww-authenticate: ${wwwAuthHeader}`,
|
||||
),
|
||||
);
|
||||
|
||||
vi.mocked(OAuthUtils.discoverOAuthFromWWWAuthenticate).mockResolvedValue(
|
||||
null,
|
||||
);
|
||||
vi.mocked(OAuthUtils.extractBaseUrl).mockReturnValue(baseUrl);
|
||||
vi.mocked(OAuthUtils.discoverOAuthConfig).mockResolvedValue({
|
||||
authorizationUrl: authUrl,
|
||||
tokenUrl,
|
||||
scopes: ['read'],
|
||||
});
|
||||
|
||||
vi.mocked(mockedClient.connect).mockResolvedValueOnce(undefined);
|
||||
|
||||
const client = await connectToMcpServer(
|
||||
'0.0.1',
|
||||
'test-server',
|
||||
{ httpUrl: serverUrl, oauth: { enabled: true } },
|
||||
false,
|
||||
workspaceContext,
|
||||
EMPTY_CONFIG,
|
||||
);
|
||||
|
||||
expect(client).toBe(mockedClient);
|
||||
expect(OAuthUtils.discoverOAuthFromWWWAuthenticate).toHaveBeenCalledWith(
|
||||
wwwAuthHeader,
|
||||
serverUrl,
|
||||
);
|
||||
expect(OAuthUtils.extractBaseUrl).toHaveBeenCalledWith(serverUrl);
|
||||
expect(OAuthUtils.discoverOAuthConfig).toHaveBeenCalledWith(baseUrl);
|
||||
expect(mockAuthProvider.authenticate).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe('connectToMcpServer - HTTP→SSE fallback', () => {
|
||||
|
||||
@@ -719,18 +719,17 @@ async function handleAutomaticOAuth(
|
||||
try {
|
||||
debugLogger.log(`🔐 '${mcpServerName}' requires OAuth authentication`);
|
||||
|
||||
// Always try to parse the resource metadata URI from the www-authenticate header
|
||||
let oauthConfig;
|
||||
const resourceMetadataUri =
|
||||
OAuthUtils.parseWWWAuthenticateHeader(wwwAuthenticate);
|
||||
if (resourceMetadataUri) {
|
||||
oauthConfig = await OAuthUtils.discoverOAuthConfig(resourceMetadataUri);
|
||||
} else if (hasNetworkTransport(mcpServerConfig)) {
|
||||
const serverUrl = mcpServerConfig.httpUrl || mcpServerConfig.url;
|
||||
|
||||
// Try to discover OAuth config from the WWW-Authenticate header first
|
||||
let oauthConfig = await OAuthUtils.discoverOAuthFromWWWAuthenticate(
|
||||
wwwAuthenticate,
|
||||
serverUrl,
|
||||
);
|
||||
|
||||
if (!oauthConfig && hasNetworkTransport(mcpServerConfig)) {
|
||||
// Fallback: try to discover OAuth config from the base URL
|
||||
const serverUrl = new URL(
|
||||
mcpServerConfig.httpUrl || mcpServerConfig.url!,
|
||||
);
|
||||
const baseUrl = `${serverUrl.protocol}//${serverUrl.host}`;
|
||||
const baseUrl = OAuthUtils.extractBaseUrl(serverUrl!);
|
||||
oauthConfig = await OAuthUtils.discoverOAuthConfig(baseUrl);
|
||||
}
|
||||
|
||||
@@ -754,8 +753,6 @@ async function handleAutomaticOAuth(
|
||||
};
|
||||
|
||||
// Perform OAuth authentication
|
||||
// Pass the server URL for proper discovery
|
||||
const serverUrl = mcpServerConfig.httpUrl || mcpServerConfig.url;
|
||||
debugLogger.log(
|
||||
`Starting OAuth authentication for server '${mcpServerName}'...`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user