feat(core): implement HTTP authentication support for A2A remote agents (#20510)

Co-authored-by: Adam Weidman <adamfweidman@google.com>
This commit is contained in:
Sandy Tao
2026-03-02 11:59:48 -08:00
committed by GitHub
parent 48412a068e
commit 446a4316c4
11 changed files with 565 additions and 27 deletions
@@ -20,14 +20,22 @@ import {
} from './a2a-client-manager.js';
import type { RemoteAgentDefinition } from './types.js';
import { createMockMessageBus } from '../test-utils/mock-message-bus.js';
import { A2AAuthProviderFactory } from './auth-provider/factory.js';
import type { A2AAuthProvider } from './auth-provider/types.js';
// Mock A2AClientManager
vi.mock('./a2a-client-manager.js', () => {
const A2AClientManager = {
vi.mock('./a2a-client-manager.js', () => ({
A2AClientManager: {
getInstance: vi.fn(),
};
return { A2AClientManager };
});
},
}));
// Mock A2AAuthProviderFactory
vi.mock('./auth-provider/factory.js', () => ({
A2AAuthProviderFactory: {
create: vi.fn(),
},
}));
describe('RemoteAgentInvocation', () => {
const mockDefinition: RemoteAgentDefinition = {
@@ -118,7 +126,7 @@ describe('RemoteAgentInvocation', () => {
});
describe('Execution Logic', () => {
it('should lazy load the agent with ADCHandler if not present', async () => {
it('should lazy load the agent without auth handler when no auth configured', async () => {
mockClientManager.getClient.mockReturnValue(undefined);
mockClientManager.sendMessageStream.mockImplementation(
async function* () {
@@ -143,10 +151,80 @@ describe('RemoteAgentInvocation', () => {
expect(mockClientManager.loadAgent).toHaveBeenCalledWith(
'test-agent',
'http://test-agent/card',
expect.objectContaining({
headers: expect.any(Function),
shouldRetryWithHeaders: expect.any(Function),
}),
undefined,
);
});
it('should use A2AAuthProviderFactory when auth is present in definition', async () => {
const mockAuth = {
type: 'http' as const,
scheme: 'Basic' as const,
username: 'admin',
password: 'password',
};
const authDefinition: RemoteAgentDefinition = {
...mockDefinition,
auth: mockAuth,
};
const mockHandler = {
type: 'http' as const,
headers: vi.fn().mockResolvedValue({ Authorization: 'Basic dGVzdA==' }),
shouldRetryWithHeaders: vi.fn(),
} as unknown as A2AAuthProvider;
(A2AAuthProviderFactory.create as Mock).mockResolvedValue(mockHandler);
mockClientManager.getClient.mockReturnValue(undefined);
mockClientManager.sendMessageStream.mockImplementation(
async function* () {
yield {
kind: 'message',
messageId: 'msg-1',
role: 'agent',
parts: [{ kind: 'text', text: 'Hello' }],
};
},
);
const invocation = new RemoteAgentInvocation(
authDefinition,
{ query: 'hi' },
mockMessageBus,
);
await invocation.execute(new AbortController().signal);
expect(A2AAuthProviderFactory.create).toHaveBeenCalledWith({
authConfig: mockAuth,
agentName: 'test-agent',
});
expect(mockClientManager.loadAgent).toHaveBeenCalledWith(
'test-agent',
'http://test-agent/card',
mockHandler,
);
});
it('should return error when auth provider factory returns undefined for configured auth', async () => {
const authDefinition: RemoteAgentDefinition = {
...mockDefinition,
auth: {
type: 'http' as const,
scheme: 'Bearer' as const,
token: 'secret-token',
},
};
(A2AAuthProviderFactory.create as Mock).mockResolvedValue(undefined);
mockClientManager.getClient.mockReturnValue(undefined);
const invocation = new RemoteAgentInvocation(
authDefinition,
{ query: 'hi' },
mockMessageBus,
);
const result = await invocation.execute(new AbortController().signal);
expect(result.error?.message).toContain(
"Failed to create auth provider for agent 'test-agent'",
);
});