Skip to content

fix(core): prevent OAuth IdP mix-up in MCP authentication (463963247) - #29117

Open
jvargassanchez-dot wants to merge 4 commits into
google-gemini:mainfrom
jvargassanchez-dot:b_463963247
Open

fix(core): prevent OAuth IdP mix-up in MCP authentication (463963247)#29117
jvargassanchez-dot wants to merge 4 commits into
google-gemini:mainfrom
jvargassanchez-dot:b_463963247

Conversation

@jvargassanchez-dot

@jvargassanchez-dot jvargassanchez-dot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements RFC 9207 Authorization Server Issuer Identification validation in the OAuth callback handler to defend against Identity Provider (IdP) mix-up attacks and prevent unauthorized token leakage.

Details

  • Extended OAuthAuthorizationResponse to include the optional iss?: string parameter.
  • Implemented areIssuersEqual(issuerA, issuerB) helper in oauth-flow.ts to normalize trailing slashes and origin paths per RFC 8414/9207 while rejecting userinfo and preserving queries/fragments.
  • Updated startCallbackServer to accept expectedIssuer?: string and enforce fail-closed RFC 9207 validation (rejecting missing iss or mismatched issuers with HTTP 400).
  • Sanitized error objects and debug logs to prevent disclosing sensitive internal issuer URLs or reflecting untrusted callback inputs.
  • Connected config.issuer from MCP discovery to startCallbackServer in MCPOAuthProvider.authenticate.
  • Updated documentation in docs/tools/mcp-server.md with RFC 9207 authorization server requirements.

Compatibility Note for MCP Developers

  • Remote MCP servers using OAuth must ensure their Authorization Server supports RFC 9207 by returning the iss parameter in the callback redirect (/oauth/callback?code=...&state=...&iss=https://<issuer>).
  • If an expected issuer is configured or discovered for the MCP server, callback responses missing iss will be rejected.

Related Issues

How to Validate

  1. Run unit tests for the A2A server:
    npm test -w @google/gemini-cli-a2a-server
  2. Verify that all 149 tests pass.

Pre-Merge Checklist

  • [ x] Updated relevant documentation and README (if needed)
  • [ x] Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • [ x] Linux
      • [ x] npm run
      • npx
      • Docker

Implements RFC 9207 Authorization Server Issuer Identification validation in the OAuth callback handler to defend against Identity Provider (IdP) mix-up attacks and prevent unauthorized token leakage.
@jvargassanchez-dot
jvargassanchez-dot requested a review from a team as a code owner August 28, 2026 17:56
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 enhances the security of the OAuth authentication flow by implementing RFC 9207 validation. By verifying the issuer (iss) parameter returned during the callback, the system can now defend against Identity Provider (IdP) mix-up attacks. The changes include robust URL normalization, updated callback server logic, and comprehensive test coverage to ensure both modern and legacy authorization servers are handled correctly.

Highlights

  • RFC 9207 Implementation: Implemented Authorization Server Issuer Identification validation in the OAuth callback handler to mitigate IdP mix-up attacks.
  • Issuer Validation Logic: Added areIssuersEqual helper to normalize issuer URLs (handling trailing slashes and path differences) for robust comparison.
  • Callback Server Updates: Updated startCallbackServer to accept an expectedIssuer parameter and perform validation against the incoming iss parameter, while maintaining legacy support.
  • Observability: Added detailed debug and warning logs to track issuer validation status and identify potential security mismatches.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the 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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. 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.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the size/m A medium sized PR label Aug 28, 2026
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

📊 PR Size: size/L

  • Lines changed: 469
  • Additions: +466
  • Deletions: -3
  • Files changed: 4

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements RFC 9207 Authorization Server Issuer Identification to defend against OAuth Mix-Up attacks. It introduces an areIssuersEqual utility to compare issuer URLs and updates startCallbackServer to validate the incoming iss parameter against the expected issuer, accompanied by comprehensive unit tests. However, a security vulnerability was identified in the areIssuersEqual normalization logic: discarding the userinfo component from the URL allows an attacker to bypass the issuer check using a crafted URL (e.g., containing an @ symbol). It is recommended to apply the provided suggestion to preserve the full URL structure while stripping only query parameters, fragments, and trailing slashes.

Comment on lines +143 to +155
export function areIssuersEqual(issuerA: string, issuerB: string): boolean {
if (issuerA === issuerB) return true;
const normalize = (iss: string): string => {
try {
const u = new URL(iss);
const normalizedPath = u.pathname.replace(/\/+$/, '');
return `${u.protocol}//${u.host}${normalizedPath}`;
} catch {
return iss.replace(/\/+$/, '');
}
};
return normalize(issuerA) === normalize(issuerB);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-high high

The areIssuersEqual function's normalize helper is vulnerable to an OAuth Mix-Up attack (RFC 9207 bypass). The current implementation discards userinfo (username and password) from the URL, allowing an attacker to craft an issuer parameter like https://attacker.com@github.com/login/oauth which, after normalization, would incorrectly match the expected issuer. This could lead to the theft of authorization codes or access tokens.

To remediate this, the normalize function should preserve the full URL structure, including userinfo, while stripping query parameters, fragments, and trailing slashes. This ensures that the userinfo component is not inadvertently removed, preventing the described mix-up attack.

Note that the current normalization logic also discards query parameters (u.search) and fragments (u.hash). While RFC 8414 states that issuer URLs should not contain these, some custom or multi-tenant identity providers use them to distinguish between different tenants or environments. This could potentially lead to another form of IdP mix-up bypass if not carefully considered.

Suggested change
export function areIssuersEqual(issuerA: string, issuerB: string): boolean {
if (issuerA === issuerB) return true;
const normalize = (iss: string): string => {
try {
const u = new URL(iss);
const normalizedPath = u.pathname.replace(/\/+$/, '');
return `${u.protocol}//${u.host}${normalizedPath}`;
} catch {
return iss.replace(/\/+$/, '');
}
};
return normalize(issuerA) === normalize(issuerB);
}
export function areIssuersEqual(issuerA: string, issuerB: string): boolean {
if (issuerA === issuerB) return true;
const normalize = (iss: string): string => {
try {
const u = new URL(iss);
u.search = '';
u.hash = '';
return u.href.replace(~/+$/, '');
} catch {
return iss.replace(~/+$/, '');
}
};
return normalize(issuerA) === normalize(issuerB);
}

@gemini-cli gemini-cli Bot added the status/need-issue Pull requests that need to have an associated issue. label Aug 28, 2026
…63963247)

- Reject issuer URLs containing userinfo (username/password) to prevent RFC 9207 mix-up bypasses

- Preserve query parameters and fragments during normalization to avoid multi-tenant issuer collisions

- Normalize trailing slashes on pathname rather than href

- Add unit and callback server tests for userinfo spoofing and query parameter differentiation
@github-actions github-actions Bot added the size/l A large sized PR label Aug 28, 2026
@jvargassanchez-dot

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements RFC 9207 Authorization Server Issuer Identification to defend against OAuth mix-up attacks. It introduces an areIssuersEqual utility to normalize and compare issuer URLs, updates startCallbackServer to validate the incoming iss parameter against an expected issuer, and adds corresponding unit tests. The review feedback highlights a security downgrade vulnerability where the iss parameter is allowed to be omitted even when an expected issuer is configured; per RFC 9207, the server should strictly reject the response in this scenario to maintain a fail-closed security posture.

Comment thread packages/core/src/utils/oauth-flow.ts Outdated
Comment on lines +294 to +298
} else if (expectedIssuer && !iss) {
debugLogger.debug(
`OAuth callback omitted "iss" parameter; expected "${expectedIssuer}". Proceeding for backwards compatibility with legacy authorization servers.`,
);
} else if (iss) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-high high

Allowing the iss parameter to be omitted when an expectedIssuer is configured introduces a downgrade vulnerability that defeats the RFC 9207 mix-up defense. An attacker performing an IdP mix-up attack can simply strip the iss parameter from the authorization response to bypass this check entirely.

According to RFC 9207 Section 2.1:

If the client has been configured to expect a particular issuer identifier, and the authorization response does not contain the 'iss' parameter, the client MUST reject the response.

To maintain robust security, we should strictly reject the response by default when the expected issuer is missing, ensuring security checks are implemented in a 'fail-closed' manner.

            } else if (expectedIssuer && !iss) {
              debugLogger.error(
                'OAuth callback omitted "iss" parameter; expected "' + expectedIssuer + '". Rejecting response per RFC 9207 Section 2.1.',
              );
              res.writeHead(400, { 'Content-Type': 'text/html' });
              res.end(
                '<html>' +
                '<body>' +
                '<h1>Authentication Failed</h1>' +
                '<p>Error: Missing issuer parameter in response.</p>' +
                '<p>You can close this window.</p>' +
                '</body>' +
                '</html>'
              );
              server.close();
              reject(
                new Error(
                  'Missing "iss" parameter in response; expected "' + expectedIssuer + '" per RFC 9207',
                ),
              );
              return;
            }
References
  1. Security checks should be implemented in a 'fail-closed' manner. If an item's validity cannot be verified (e.g., due to missing metadata or parameters), it should be rejected by default.

…e error output (b/463963247)

- Reject callbacks when expectedIssuer is configured but iss is missing to prevent downgrade attacks

- Sanitize error messages and debug logs by removing raw issuer URLs and sensitive configuration details

- Add tests covering fail-closed missing iss behavior and information disclosure prevention
@jvargassanchez-dot

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements RFC 9207 Authorization Server Issuer Identification and Mix-Up defense in the OAuth flow. It introduces the areIssuersEqual utility function to safely compare issuer URLs by normalizing trailing slashes and origins, rejecting userinfo, and preserving query parameters and fragments. The startCallbackServer function is updated to accept an optional expectedIssuer parameter and enforce issuer validation when configured, rejecting authorization responses that lack a matching iss parameter. Comprehensive unit tests have been added to verify these security checks and normalization behaviors. There are no review comments, and I have no additional feedback to provide.

…th servers (b/463963247)

- Add authorization server requirements and callback redirect examples for RFC 9207

- Add configuration example with explicit issuer in settings.json

- Document issuer property under OAuth configuration properties
@jvargassanchez-dot
jvargassanchez-dot requested a review from a team as a code owner August 28, 2026 21:37
@jvargassanchez-dot jvargassanchez-dot changed the title fix(core): prevent OAuth IdP mix-up in MCP authentication (b/463963247) fix(core): prevent OAuth IdP mix-up in MCP authentication (463963247) Aug 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l A large sized PR size/m A medium sized PR status/need-issue Pull requests that need to have an associated issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant