feat(core): add OAuth2 Authorization Code auth provider for A2A agents#21496
feat(core): add OAuth2 Authorization Code auth provider for A2A agents#21496adamfweidman merged 11 commits intomainfrom
Conversation
…vider Extract generic OAuth 2.0 Authorization Code + PKCE logic into a shared oauth-flow.ts module so both MCP and A2A authentication providers can reuse it. MCPOAuthProvider now delegates to these shared utilities while retaining MCP-specific logic (discovery, dynamic registration, resource parameter handling). Part of #17600
Address review feedback: validate that JSON-parsed response contains a string access_token before treating it as an OAuthTokenResponse. If the JSON doesn't contain the expected field, fall through to form-urlencoded parsing instead of blindly casting.
- Replace unsafe clientId! non-null assertion in MCPOAuthProvider.refreshAccessToken with an explicit validation guard - Add 42 dedicated unit tests for oauth-flow.ts covering all exported functions: generatePKCEParams, getPortFromUrl, buildAuthorizationUrl, startCallbackServer, exchangeCodeForToken, and refreshAccessToken
Implement OAuth2AuthProvider with PKCE support for A2A remote agents, enabling browser-based OAuth 2.0 Authorization Code flow with automatic token refresh and persistent storage. Key changes: - OAuth2AuthProvider: full auth code + PKCE flow with token caching - Agent card URL discovery: fetches OAuth endpoints from agent card - Separate token storage: a2a-oauth-tokens.json (not MCP's file) - Zod schema + frontmatter support for oauth2 auth type - Factory wiring with dynamic import to avoid module graph issues
Separate the fetch implementations used for agent card resolution vs. API transport calls. By default, the agent card is fetched without auth headers to avoid servers that reject unexpected credentials (e.g. 400). Auth is only sent to the card endpoint when agent_card_requires_auth is explicitly set to true in the agent definition.
|
Hi there! Thank you for your contribution to Gemini CLI. To improve our contribution process and better track changes, we now require all pull requests to be associated with an existing issue, as announced in our recent discussion and as detailed in our CONTRIBUTING.md. This pull request is being closed because it is not currently linked to an issue. Once you have updated the description of this PR to link an issue (e.g., by adding How to link an issue: Thank you for your understanding and for being a part of our community! |
|
Hi @SandyTao520, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this. We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: Improving Our Contribution Process & Introducing New Guidelines. Key Update: Starting January 26, 2026, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed. Thank you for your understanding and for being a part of our community! |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the authentication capabilities for A2A remote agents by integrating the OAuth 2.0 Authorization Code flow with PKCE. It achieves this by refactoring existing OAuth logic into reusable primitives and introducing a dedicated provider for A2A agents. The changes also improve the robustness of agent card fetching by handling authentication requirements more intelligently, preventing potential credential rejection issues. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the authentication capabilities for A2A (Agent-to-Agent) remote agents by integrating OAuth2 Authorization Code with PKCE. It centralizes common OAuth logic into a shared utility, enabling more robust and secure communication for agents while also refining how agent cards are fetched to improve compatibility and reduce authentication-related errors. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces OAuth2 Authorization Code + PKCE authentication for A2A remote agents. The implementation is robust, featuring a comprehensive new OAuth2AuthProvider that handles the full interactive flow, token refresh, and persistent storage. A significant part of this work involved refactoring shared OAuth logic from the existing MCP provider into a new, well-tested utils/oauth-flow.ts module, which greatly improves code structure and reusability. The changes also include a fix to agent card resolution to avoid sending authentication headers by default. The code quality is high, with thorough testing and good design patterns. I have no major concerns.
|
Size Change: +11.7 kB (+0.04%) Total Size: 26.2 MB
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Code Review
This pull request introduces OAuth2 Authorization Code + PKCE support for A2A agents, involving refactoring existing OAuth logic into shared primitives, adding the new OAuth2AuthProvider, and updating the agent loading mechanism to handle authentication for agent cards. A high-severity vulnerability was identified where OAuth2 client credentials could be leaked to an untrusted origin due to unvalidated endpoint discovery from remote agent cards; implementing origin validation for discovered OAuth2 endpoints is recommended. Additionally, the shared OAuth flow utility could benefit from more robust error handling.
I am having trouble creating individual review comments. Click here to see my feedback.
packages/core/src/agents/auth-provider/oauth2-provider.ts (231-238)
The OAuth2AuthProvider automatically discovers OAuth2 endpoints (authorizationUrl and tokenUrl) from the remote agent's card without validating that they belong to a trusted origin. If a user configures a remote agent with a client_secret but does not explicitly provide the endpoints in their local configuration, the CLI will discover them from the agent's card and send the client_secret to the discovered tokenUrl. A malicious or compromised agent could provide a tokenUrl pointing to an attacker-controlled server, leading to the theft of the user's OAuth2 client credentials.
Recommendation: Validate that discovered OAuth2 endpoints belong to the same origin as the agentCardUrl. If they differ, require the user to explicitly provide the endpoints in their local configuration or prompt for confirmation before sending sensitive credentials.
packages/core/src/utils/oauth-flow.ts (358-409)
The current token response parsing logic can be improved for robustness. If an OAuth server returns a 200 OK status with a JSON error payload (e.g., {"error":"invalid_grant"}), the current implementation will fall through to the form-urlencoded parser and produce a generic no_access_token error, obscuring the actual cause.
This suggestion refactors the parsing to:
- Handle JSON error objects correctly, even with a
200 OKstatus. - Simplify the success-case JSON parsing.
- Ensure that specific errors from the server are propagated, which significantly aids in debugging authentication issues.
try {
const data = JSON.parse(responseText) as Record<string, unknown>;
if (typeof data?.access_token === 'string') {
return {
access_token: data.access_token,
token_type: (data.token_type as string) || 'Bearer',
expires_in: data.expires_in as number | undefined,
refresh_token: data.refresh_token as string | undefined,
scope: data.scope as string | undefined,
};
}
if (typeof data?.error === 'string') {
throw new Error(
`${operationName} failed: ${data.error} - ${data.error_description || 'No description'}`,
);
}
} catch (e) {
if (e instanceof Error && e.message.startsWith(operationName)) {
throw e; // Rethrow our specific error
}
// Not valid JSON or not a recognized structure, fall through to form-urlencoded
}
// Parse form-urlencoded response
const tokenParams = new URLSearchParams(responseText);
const accessToken = tokenParams.get('access_token');
const tokenType = tokenParams.get('token_type') || 'Bearer';
const expiresIn = tokenParams.get('expires_in');
const refreshToken = tokenParams.get('refresh_token');
const scope = tokenParams.get('scope');
if (!accessToken) {
// Check for error in response
const error = tokenParams.get('error');
const errorDescription = tokenParams.get('error_description');
throw new Error(
`${operationName} failed: ${error || defaultErrorCode} - ${errorDescription || responseText}`,
);
}
return {
access_token: accessToken,
token_type: tokenType,
expires_in: expiresIn ? parseInt(expiresIn, 10) : undefined,
refresh_token: refreshToken || undefined,
scope: scope || undefined,
};The OAuth2 auth provider's fetchAgentCardDefaults() was manually parsing the raw agent card JSON, which only handled the JSON Schema format. Proto-format agent cards (which use oneof field names instead of a 'type' discriminator) were not recognized, causing authorization_url and token_url discovery to silently fail. Replace the manual parsing with DefaultAgentCardResolver, which normalizes both JSON Schema and proto-format cards via the a2a-js SDK.
| * OAuth2 auth schema. | ||
| * authorization_url and token_url can be discovered from the agent card if omitted. | ||
| */ | ||
| const oauth2AuthSchema = z.object({ |
There was a problem hiding this comment.
is this resilient to adding other types of oauth in the future. e.g. dynamic client registration. These interfaces are harder to change in the future as people will have them configured on their local.
…Replace eager 'agentCardRequiresAuth' with dynamic 401/403 retry wrapper for agent card fetching in A2AClientManager.\n- Remove 'requiresAgentAuth' and 'agentCardRequiresAuth' properties entirely from AgentRegistry, TOML parsing, and A2AClientManager.\n- Prevent token collisions by isolating A2A OAuth tokens from MCP tokens in OS Keychain via custom ServiceNames.
adamfweidman
left a comment
There was a problem hiding this comment.
LGTM, waiting on a2aproject/a2a-js#344
google-gemini#21496) Co-authored-by: Adam Weidman <adamfweidman@google.com>
google-gemini#21496) Co-authored-by: Adam Weidman <adamfweidman@google.com>
google-gemini#21496) Co-authored-by: Adam Weidman <adamfweidman@google.com>
#21496) Co-authored-by: Adam Weidman <adamfweidman@google.com>
google-gemini#21496) Co-authored-by: Adam Weidman <adamfweidman@google.com>
Summary
Adds OAuth2 Authorization Code + PKCE authentication support for A2A remote agents, built on shared OAuth flow primitives extracted from the existing MCP OAuth provider.
Details
This PR consists of three logical parts:
1. Extract shared OAuth flow primitives (
utils/oauth-flow.ts)Refactors the MCP OAuth provider to extract reusable OAuth primitives (PKCE generation, authorization URL building, token exchange, token refresh, callback server) into
utils/oauth-flow.ts. The MCP provider is updated to use these shared primitives, with no behavioral changes.2. Add OAuth2 auth provider for A2A agents
Introduces
OAuth2AuthProviderthat implements the full OAuth 2.0 Authorization Code + PKCE flow for A2A remote agents:~/.gemini/a2a-oauth-tokens.json(separate from MCP tokens)securitySchemes(with fallback to user config)Also adds:
oauth2auth type in agent definitions3. Fix agent card fetch with auth credentials
Separates the fetch implementation used for agent card resolution from API transport calls. By default, the agent card is fetched without auth headers to avoid servers that reject unexpected credentials (e.g., 400 errors). Auth is only sent to the card endpoint when
agent_card_requires_authis explicitly set totrue.Commits
refactor(core): extract shared OAuth flow primitives from MCPOAuthProviderfix(core): validate JSON token response before castingfix(core): add clientId guard and unit tests for shared OAuth flowfeat(core): add OAuth2 Authorization Code auth provider for A2A agentsfix(core): use unauthenticated fetch for A2A agent card resolutionRelated Issues
Fixes #17600
How to Validate
auth.type: oauth2and valid Google OAuth credentials. Verify the browser-based auth flow completes and tokens are persisted.Pre-Merge Checklist