Skip to content

fix(auth): defensively inject response_type=code in OAuth URL (#695)#737

Open
derek3078 wants to merge 1 commit intogoogleworkspace:mainfrom
derek3078:fix/auth-login-response-type-695
Open

fix(auth): defensively inject response_type=code in OAuth URL (#695)#737
derek3078 wants to merge 1 commit intogoogleworkspace:mainfrom
derek3078:fix/auth-login-response-type-695

Conversation

@derek3078
Copy link
Copy Markdown

Summary

Addresses the symptom described in #695: gws auth login fails with
"Error 400: invalid_request, Required parameter is missing:
response_type" on macOS Desktop OAuth clients. Adds a defensive
injection of response_type=code in CliFlowDelegate::present_user_url
so the URL presented to the user is always well-formed.

What this changes

  • crates/google-workspace-cli/src/auth_commands.rs: in
    CliFlowDelegate::present_user_url, if the URL received from
    yup_oauth2 does not contain response_type=code, append it before
    presenting to the user.
  • No change to dependency versions, flow selection (has_proxy_env),
    or any other code path.
  • No-op when the URL is already well-formed.

Why not a deeper fix

yup_oauth2 v12.1.2's build_authentication_request_url includes
"&response_type=code" in its params vector. Reading the source
suggests the URL reaching the delegate should be correct. Source
inspection alone did not reproduce the missing-parameter condition.
The empirical report in #695, including @diegobartra's direct-URL
reproduction in a browser, nonetheless establishes that affected users
do see the parameter missing.

This PR is intentionally narrow: it fixes the symptom reliably without
changing flow selection or dependencies. The deeper root cause (why
yup_oauth2's params fold does not always reach the delegate) is worth
investigation but does not block this fix.

Verification

Verified locally on macOS aarch64 with rustc 1.95.0 (stable):

  • cargo check on the full workspace: clean, no warnings.
  • cargo test --package google-workspace-cli auth_commands: 68 passed,
    0 failed, 0 ignored.
  • yup_oauth2 v12.1.2 source reviewed for compatibility with both
    ask_auth_code_interactively and ask_auth_code_via_http code paths.

Credit

Diagnostic and root-cause narrative by @diegobartra in #695. This PR
implements a defensive workaround for the symptom they described.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 20, 2026

⚠️ No Changeset found

Latest commit: d8be797

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@google-cla
Copy link
Copy Markdown

google-cla Bot commented Apr 20, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@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 introduces a defensive measure to ensure that OAuth authentication URLs are correctly formatted before being displayed to the user. By explicitly injecting the response_type=code parameter when it is missing, the change prevents authentication failures reported by users on specific platforms, while maintaining existing functionality for correctly formed URLs.

Highlights

  • Defensive URL Injection: Added a check in CliFlowDelegate::present_user_url to ensure response_type=code is present in the OAuth URL before it is presented to the user.
  • Bug Mitigation: Provides a targeted workaround for an issue where some OAuth clients on macOS were receiving malformed URLs missing the required response_type parameter.
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 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 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.

Footnotes

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

Copy link
Copy Markdown
Contributor

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

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 introduces defensive logic to ensure the response_type=code parameter is present in the OAuth authentication URL, addressing reported issues where Google rejects requests due to a missing parameter. While the fix addresses the core issue, the current implementation of URL manipulation is fragile as it does not account for URL fragments (#) and uses simple substring matching which could lead to malformed URLs. A more robust approach for parsing and rebuilding the URL is recommended.

Comment on lines 562 to 584
let url_with_response_type = if url.contains("response_type=code") {
url.to_string()
} else if url.contains('?') {
format!("{url}&response_type=code")
} else {
format!("{url}?response_type=code")
};

// Inject login_hint into the OAuth URL if we have one
let display_url = if let Some(ref hint) = self.login_hint {
let encoded: String = percent_encoding::percent_encode(
hint.as_bytes(),
percent_encoding::NON_ALPHANUMERIC,
)
.to_string();
if url.contains('?') {
format!("{url}&login_hint={encoded}")
if url_with_response_type.contains('?') {
format!("{url_with_response_type}&login_hint={encoded}")
} else {
format!("{url}?login_hint={encoded}")
format!("{url_with_response_type}?login_hint={encoded}")
}
} else {
url.to_string()
url_with_response_type
};
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.

high

The current implementation of URL parameter injection is fragile and can produce malformed URLs if the input contains a fragment (#) or if the substring response_type=code appears elsewhere in the URL (e.g., as part of another parameter's value). Appending parameters directly to the end of the string will incorrectly place them inside the fragment if one is present, making them invisible to the server.

A more robust approach is to separate the fragment before manipulation and use a more precise check for the existence of the response_type parameter. Since the code ensures response_type=code is always present (either by finding it or adding it), any subsequent parameters like login_hint can safely be appended using &.

            let (base, fragment) = match url.split_once('#') {
                Some((b, f)) => (b, Some(f)),
                None => (url, None),
            };

            let mut display_url = if base.split(['?', '&']).any(|p| p == "response_type=code") {
                base.to_string()
            } else if base.contains('?') {
                format!("{base}&response_type=code")
            } else {
                format!("{base}?response_type=code")
            };

            if let Some(ref hint) = self.login_hint {
                let encoded = percent_encoding::percent_encode(
                    hint.as_bytes(),
                    percent_encoding::NON_ALPHANUMERIC,
                );
                display_url = format!("{display_url}&login_hint={encoded}");
            }

            if let Some(f) = fragment {
                display_url.push('#');
                display_url.push_str(f);
            }

…workspace#695)

Issue googleworkspace#695 reports that `gws auth login` produces an authorization URL
missing `response_type=code`, causing Google to reject with
"Error 400: invalid_request, Required parameter is missing:
response_type". Reproduces on macOS (Darwin arm64) with Desktop OAuth
clients on v0.22.4 and v0.22.5.

yup_oauth2 v12.1.2's `build_authentication_request_url` does include
`&response_type=code` in its params vector, so code review of the URL
builder alone suggests the URL reaching the delegate should be correct.
The empirical report in googleworkspace#695 nonetheless shows the parameter missing at
the point Google receives the request, verified by @diegobartra by
constructing a correctly-formed URL by hand and watching the consent
screen render against the same Desktop OAuth client.

This change makes `CliFlowDelegate::present_user_url` inject
`response_type=code` defensively when it is absent from the URL. When
the upstream URL is correct, this is a no-op. When it is not, this
unblocks the user.

This PR is intentionally narrow: it fixes the symptom reliably without
changing flow selection or dependencies. The deeper root cause (why
yup_oauth2's params fold does not always reach the delegate) is worth
investigation but does not block this fix.

Credit to @diegobartra for the diagnostic in googleworkspace#695.

Updated per gemini-code-assist review: handle URL fragments by
splitting on '#' before manipulation, and use exact-parameter
equality check instead of substring match. URL-rewriting logic
extracted into a pure helper function with unit tests covering
the edge cases Gemini's review identified.

Verified locally on macOS aarch64 with rustc 1.95.0:
- cargo check: clean, no warnings
- cargo test --package google-workspace-cli auth_commands: 76 passed,
  0 failed, 0 ignored

Addresses googleworkspace#695
@derek3078 derek3078 force-pushed the fix/auth-login-response-type-695 branch from bd05713 to d8be797 Compare April 20, 2026 23:06
@googleworkspace-bot
Copy link
Copy Markdown
Collaborator

/gemini review

Copy link
Copy Markdown
Contributor

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

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 introduces a defensive build_display_url function to ensure the response_type=code parameter is present in OAuth authentication URLs, addressing issues where it might be missing on certain platforms. It also refactors the CliFlowDelegate to use this function for URL construction and adds a comprehensive suite of unit tests to verify correct handling of query parameters, fragments, and login hints. I have no feedback to provide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants