Skip to content

fix(executor): skip x-goog-user-project header for OAuth auth method#827

Open
nuthalapativarun wants to merge 4 commits into
googleworkspace:mainfrom
nuthalapativarun:fix/729-skip-quota-project-for-oauth
Open

fix(executor): skip x-goog-user-project header for OAuth auth method#827
nuthalapativarun wants to merge 4 commits into
googleworkspace:mainfrom
nuthalapativarun:fix/729-skip-quota-project-for-oauth

Conversation

@nuthalapativarun
Copy link
Copy Markdown

Summary

The x-goog-user-project header was sent unconditionally. When project_id is present in client_secret.json, this triggers IAM checks, causing 403 errors for OAuth users who are not IAM members of that project.

Fix: only send the header when auth_method != AuthMethod::OAuth.

Fixes #729

Checklist

  • cargo fmt --all passed
  • cargo clippy -- -D warnings passed
  • cargo test passed
  • Changeset file added (patch bump)

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 1, 2026

🦋 Changeset detected

Latest commit: edc245f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@googleworkspace/cli Patch

Not sure what this means? Click here to learn what changesets are.

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

@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 addresses an issue where the 'x-goog-user-project' header was being sent unconditionally, causing 403 permission errors for OAuth users who are not IAM members of the specified project. By restricting this header to non-OAuth authentication methods, the CLI now correctly handles project-level quota attribution without violating IAM constraints.

Highlights

  • Conditional Header Logic: Updated the HTTP request builder to conditionally include the 'x-goog-user-project' header only when the authentication method is not OAuth.
  • Regression Testing: Added a new unit test to verify that OAuth requests correctly omit the 'x-goog-user-project' header, preventing 403 errors for non-IAM project members.
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 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 aims to skip sending the x-goog-user-project header during OAuth authentication to prevent 403 errors for non-project-member users, and adds a unit test to verify this behavior. However, feedback indicates that both user OAuth and Service Account/ADC authentication paths utilize AuthMethod::OAuth to attach bearer tokens. Consequently, the check *auth_method != AuthMethod::OAuth will incorrectly prevent the header from being sent for ADC or service account authentication as well, breaking the intended behavior.

Comment thread crates/google-workspace-cli/src/executor.rs Outdated
…ta header

AuthMethod::OAuth covers both user OAuth and service-account credentials,
so the previous check (*auth_method != AuthMethod::OAuth) would incorrectly
suppress the x-goog-user-project header for service accounts (which do need
it) while also setting it on unauthenticated (None) requests.

Add AuthMethod::ServiceAccount and auth::CredentialKind so the call site in
main.rs can tag the request with the right variant. The quota header is now
only sent for ServiceAccount auth; user OAuth requests remain header-free to
avoid 403 errors for users who are not IAM members of the project.
@googleworkspace-bot googleworkspace-bot added area: auth area: core Core CLI parsing, commands, error handling, utilities labels Jun 1, 2026
@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 prevents 403 errors for non-project-member users by skipping the x-goog-user-project header during OAuth authentication. It introduces a CredentialKind enum to track the credential type and updates the request builder to only attach the header for service-account credentials. The review feedback suggests a valuable refactoring to eliminate redundancy: instead of introducing CredentialKind, the existing AuthMethod enum should be moved to auth.rs, re-exported in executor.rs, and returned directly by get_token_with_kind. This change would simplify the codebase and remove unnecessary boilerplate mapping in main.rs.

Comment thread crates/google-workspace-cli/src/auth.rs Outdated
Comment thread crates/google-workspace-cli/src/auth.rs Outdated
Comment thread crates/google-workspace-cli/src/executor.rs Outdated
Comment thread crates/google-workspace-cli/src/main.rs Outdated
Replace the separate CredentialKind enum with the existing AuthMethod
enum (moved from executor.rs to auth.rs so authentication types live
with authentication code).  get_token_with_kind now returns AuthMethod
directly, eliminating the mapping boilerplate in main.rs.

Resolves Gemini r2 comment on PR googleworkspace#827.
@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 updates the CLI to skip sending the "x-goog-user-project" header for OAuth authentication, preventing 403 errors for users who are not IAM members of the project, while retaining it for service-account authentication. To support this, an "AuthMethod" enum and a "get_token_with_kind" function are introduced. The review feedback highlights that completely omitting this header for OAuth requests breaks the documented behavior of the "GOOGLE_WORKSPACE_PROJECT_ID" environment variable, and suggests allowing explicit overrides via this variable.

Comment thread crates/google-workspace-cli/src/executor.rs Outdated
…icitly set

The previous fix completely omitted x-goog-user-project for OAuth, which
broke the documented GOOGLE_WORKSPACE_PROJECT_ID env var behaviour. Users
who explicitly set that variable expect the header to be forwarded.

New behaviour:
- ServiceAccount: always send the header (env var, config, or ADC)
- OAuth: only send when GOOGLE_WORKSPACE_PROJECT_ID is explicitly set
- None: never send

Also serialise the two env-var-mutating tests with a static Mutex to
avoid races when the test binary runs threads in parallel.
@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 updates the Google Workspace CLI to skip the x-goog-user-project quota header by default during OAuth authentication, preventing 403 errors for users who are not members of the project. It introduces an AuthMethod enum to track the authentication type and adds get_token_with_kind to retrieve both the token and its authentication method. The feedback suggests refactoring get_token to delegate to get_token_with_kind to eliminate code duplication. Additionally, it is recommended to replace the local mutex in the new tests with #[serial_test::serial] to avoid flaky test runs caused by concurrent environment variable modifications across different test files.


/// Like [`get_token`] but also returns the [`AuthMethod`] so callers can
/// decide whether to include the `x-goog-user-project` quota header.
pub async fn get_token_with_kind(scopes: &[&str]) -> anyhow::Result<(String, AuthMethod)> {
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 implementation of get_token_with_kind duplicates the entire token loading and fetching logic of get_token. To improve maintainability and avoid future divergence, get_token should be refactored to delegate to get_token_with_kind:

pub async fn get_token(scopes: &[&str]) -> anyhow::Result<String> {
    get_token_with_kind(scopes).await.map(|(token, _)| token)
}

Comment on lines +2406 to +2412
/// Mutex to serialise tests that mutate GOOGLE_WORKSPACE_PROJECT_ID so they
/// don't race with each other when the test binary runs its threads in parallel.
static QUOTA_PROJECT_ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());

#[tokio::test]
async fn test_oauth_auth_does_not_set_quota_project_header_by_default() {
let _guard = QUOTA_PROJECT_ENV_MUTEX.lock().unwrap();
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

Using a local QUOTA_PROJECT_ENV_MUTEX only serializes tests within executor.rs. However, tests in auth.rs (such as test_get_quota_project_priority_env_var) also mutate and read the process-wide GOOGLE_WORKSPACE_PROJECT_ID environment variable and are marked with #[serial_test::serial]. Because serial_test only serializes tests marked with #[serial], these tests will run in parallel with the auth.rs tests, leading to flaky test failures due to concurrent environment variable modification.

To prevent this, remove the local mutex and use #[serial_test::serial] on both tests to ensure global serialization across all tests in the test suite.

#[tokio::test]
#[serial_test::serial]
async fn test_oauth_auth_does_not_set_quota_project_header_by_default() {

Comment on lines +2450 to +2452
#[tokio::test]
async fn test_oauth_auth_sends_quota_project_when_env_var_explicitly_set() {
let _guard = QUOTA_PROJECT_ENV_MUTEX.lock().unwrap();
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

Use #[serial_test::serial] to serialize this test globally and remove the local mutex guard.

Suggested change
#[tokio::test]
async fn test_oauth_auth_sends_quota_project_when_env_var_explicitly_set() {
let _guard = QUOTA_PROJECT_ENV_MUTEX.lock().unwrap();
#[tokio::test]
#[serial_test::serial]
async fn test_oauth_auth_sends_quota_project_when_env_var_explicitly_set() {

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

Labels

area: auth area: core Core CLI parsing, commands, error handling, utilities area: http

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Drive API returns 403 for non-project-member users due to unnecessary x-goog-user-project header

2 participants