Skip to content

feat(auth): add gws auth use-adc command #99

Draft
kylegallatin wants to merge 2 commits intogoogleworkspace:mainfrom
kylegallatin:feat/auth-use-adc
Draft

feat(auth): add gws auth use-adc command #99
kylegallatin wants to merge 2 commits intogoogleworkspace:mainfrom
kylegallatin:feat/auth-use-adc

Conversation

@kylegallatin
Copy link

Description

Adds a new gws auth use-adc command that simplifies team authentication by importing Application Default Credentials from gcloud. This eliminates the need to create
and share custom OAuth clients.

Usage

# 1. Authenticate with ADC - choose your scopes
gcloud auth application-default login --project=my-project \
  --scopes=openid,https://www.googleapis.com/auth/userinfo.email,\
https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/gmail.modify

# 2. Import credentials to gws
gws auth use-adc

# 3. Use gws commands
gws drive files list --params '{"pageSize": 5}'

Benefits

- ✅ Simpler team onboarding (2 commands instead of manual OAuth client setup)
- ✅ Uses Google's built-in OAuth client (no custom client creation needed)
- ✅ Proper quota project tracking to the specified GCP project
- ✅ Each team member uses their own Google Workspace credentials
- ✅ No network calls during import - credentials validated on first API use

Technical Changes

src/auth_commands.rs:
- New handle_use_adc() function (~80 lines)
- Reads ADC credentials from standard gcloud paths
- Validates required fields (client_id, client_secret, refresh_token, type)
- Copies quota_project_id if present
- Saves to gws encrypted credentials

src/executor.rs:
- New get_quota_project_id() helper function
- Request builder includes x-goog-user-project header when quota project is set
- Enables proper API quota tracking for ADC credentials

README.md:
- Added "Team / Shared Setup" section with usage examples
- Documents scope selection options

Testing

Manually tested:
1. gcloud auth application-default login with Workspace scopes ✅
2. gws auth use-adc successfully imports credentials ✅
3. gws drive files list works with imported credentials ✅
4. Quota project header is included in API requests ✅
5. Works with both ~/.config/gcloud/ and ~/Library/Application Support/gcloud/ paths ✅

Test output:
{
  "credentials_file": "/Users/user/Library/Application Support/gws/credentials.enc",
  "message": "ADC credentials imported successfully.",
  "quota_project_id": "my-project",
  "status": "success"
}

Dry Run Output:
N/A - This is an auth configuration command, not an API call

Design Decisions

Why no scope validation?
- User controls scopes via gcloud auth application-default login --scopes=...
- Credentials are validated on first API use anyway
- Keeps implementation simple (no network calls)
- Matches philosophy: trust the user's configuration

Why import instead of pointing to ADC directly?
- Consistent with existing gws auth login flow
- Works with gws's existing encrypted credential storage
- Allows gws to work even if ADC credentials are later rotated

Checklist:

- My code follows the AGENTS.md guidelines (no generated google-* crates).
- I have run cargo fmt --all to format the code perfectly.
- I have run cargo clippy -- -D warnings and resolved all warnings. (Will pass in CI - local toolchain version mismatch)
- I have added tests that prove my fix is effective or that my feature works. (Open to suggestions on testing approach - async function with file I/O)
- I have provided a Changeset file (.changeset/add-auth-use-adc.md)

Adds support for importing Application Default Credentials from gcloud,
eliminating the need for custom OAuth clients in team setups.

Features:
- New `gws auth use-adc` command to import ADC credentials
- Includes quota_project_id support for proper API quota tracking
- Adds x-goog-user-project header to API requests
- Simplifies team onboarding to 2 commands
- No network validation - credentials validated on first API use

Technical changes:
- New handle_use_adc() function in src/auth_commands.rs (simple import, no network calls)
- Added get_quota_project_id() helper in src/executor.rs
- Request builder includes quota project header when available
- Supports both ~/.config/gcloud/ and ~/Library/Application Support/gcloud/ paths

This makes team authentication simpler - everyone uses Google's built-in
OAuth client instead of creating/sharing custom clients.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 5, 2026

🦋 Changeset detected

Latest commit: 611728d

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

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
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 significant enhancement to the authentication workflow by integrating Google's Application Default Credentials (ADC). It provides a new command that allows users to import their gcloud ADC directly into gws, making team authentication much simpler and more robust. This change removes the dependency on custom OAuth client setups and ensures that API requests are correctly attributed to a quota project, improving overall manageability and compliance for shared environments.

Highlights

  • New gws auth use-adc command: A new command has been added to simplify team authentication by importing Application Default Credentials (ADC) from gcloud.
  • Simplified Team Onboarding: This feature eliminates the need for creating and sharing custom OAuth clients, streamlining the setup process for teams.
  • Quota Project Tracking: The imported ADC credentials now support quota_project_id, ensuring proper API quota tracking to the specified GCP project via the x-goog-user-project header.
  • ADC Path Support: The command automatically detects and imports ADC credentials from common gcloud configuration paths, including ~/.config/gcloud/ and ~/Library/Application Support/gcloud/.
Changelog
  • .changeset/add-auth-use-adc.md
    • Added a new changeset file documenting the gws auth use-adc feature.
  • README.md
    • Updated the authentication section to include a new 'Team / Shared Setup' guide with instructions for gws auth use-adc.
    • Provided examples for choosing appropriate Workspace scopes.
  • src/auth_commands.rs
    • Implemented the handle_use_adc asynchronous function to manage the import of ADC credentials.
    • Modified the handle_auth_command function to include and dispatch to the new use-adc subcommand.
    • Updated the usage string to reflect the new use-adc option.
  • src/executor.rs
    • Added a new private helper function get_quota_project_id to retrieve the quota project ID from stored credentials.
    • Modified the build_http_request function to conditionally add the x-goog-user-project header when a quota project ID is available and the authentication method is OAuth.
Activity
  • The author manually tested the new gws auth use-adc command, verifying successful credential import and API request functionality.
  • Confirmed that the quota project header is correctly included in API requests.
  • Validated functionality across both ~/.config/gcloud/ and ~/Library/Application Support/gcloud/ ADC paths.
  • The author has provided a detailed checklist indicating adherence to code guidelines, formatting, and linting, with a note about a local toolchain version mismatch for clippy warnings.
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.

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

  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.

@google-cla
Copy link

google-cla bot commented Mar 5, 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.

Copy link
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 valuable gws auth use-adc command, simplifying authentication by importing gcloud's Application Default Credentials. The implementation is solid, and the documentation updates are clear. I've identified one high-severity bug where the new logic for retrieving the quota project ID doesn't respect credentials provided via an environment variable. I've also included a few medium-severity suggestions to improve code robustness and maintainability in the new handle_use_adc function.

Note: Security Review did not run due to the size of the PR.

Comment on lines +34 to +62
fn get_quota_project_id() -> Result<String, String> {
// Check encrypted credentials first
if let Ok(contents) = crate::credential_store::load_encrypted() {
if let Ok(creds) = serde_json::from_str::<serde_json::Value>(&contents) {
if let Some(quota_project) = creds.get("quota_project_id").and_then(|v| v.as_str()) {
return Ok(quota_project.to_string());
}
}
}

// Check plaintext credentials
let config_dir = dirs::config_dir()
.unwrap_or_else(|| std::path::PathBuf::from("."))
.join("gws");
let plain_path = config_dir.join("credentials.json");

if plain_path.exists() {
if let Ok(contents) = std::fs::read_to_string(&plain_path) {
if let Ok(creds) = serde_json::from_str::<serde_json::Value>(&contents) {
if let Some(quota_project) = creds.get("quota_project_id").and_then(|v| v.as_str())
{
return Ok(quota_project.to_string());
}
}
}
}

Err("No quota_project_id found".to_string())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

This function reimplements logic for finding and parsing credentials, but it's incomplete. It doesn't check for the GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE environment variable, which is a supported way to provide credentials. This will cause get_quota_project_id to fail and the x-goog-user-project header to be omitted when credentials are set via this environment variable.

To fix this and avoid code duplication, the logic for loading credentials should be centralized and reused here. Consider moving plain_credentials_path() from auth_commands.rs to a shared module (e.g., a new config.rs) and using it here.

Comment on lines +927 to +933
let adc_path = if adc_path_config.exists() {
adc_path_config
} else if adc_path_app_support.exists() {
adc_path_app_support
} else {
adc_path_config // default for error message
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This logic for determining adc_path works, but has a small issue. If neither path exists, the subsequent error message on line 936 will only show adc_path_config, which can be misleading on platforms like Windows where the correct path is adc_path_app_support. Consider modifying this to provide a more informative error message that lists all paths that were checked.

Comment on lines +961 to +978
let client_id = adc
.get("client_id")
.and_then(|v| v.as_str())
.ok_or_else(|| GwsError::Validation("ADC credentials missing 'client_id'".to_string()))?;

let client_secret = adc
.get("client_secret")
.and_then(|v| v.as_str())
.ok_or_else(|| {
GwsError::Validation("ADC credentials missing 'client_secret'".to_string())
})?;

let refresh_token = adc
.get("refresh_token")
.and_then(|v| v.as_str())
.ok_or_else(|| {
GwsError::Validation("ADC credentials missing 'refresh_token'".to_string())
})?;
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The validation logic for client_id, client_secret, and refresh_token is repeated. This could be refactored into a helper function to reduce code duplication and improve readability. For example:

fn get_string_field<'a>(json: &'a serde_json::Value, field: &str) -> Result<&'a str, GwsError> {
    json.get(field)
        .and_then(|v| v.as_str())
        .ok_or_else(|| GwsError::Validation(format!("ADC credentials missing '{field}'")))
}

// ...
let client_id = get_string_field(&adc, "client_id")?;
let client_secret = get_string_field(&adc, "client_secret")?;
let refresh_token = get_string_field(&adc, "refresh_token")?;

Copy link
Member

@jpoehnelt jpoehnelt left a comment

Choose a reason for hiding this comment

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

This eliminates the need to create and share custom OAuth clients.

This isn't true. Most Workspace APIs will not be able to piggyback on the gcloud OAuth. However, ADC can be setup with a custom OAuth client and should support this some way or another. Much lower priority in this case though. Should check on how to accept the ADC credentials file.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@kylegallatin
Copy link
Author

This eliminates the need to create and share custom OAuth clients.

This isn't true. Most Workspace APIs will not be able to piggyback on the gcloud OAuth. However, ADC can be setup with a custom OAuth client and should support this some way or another. Much lower priority in this case though. Should check on how to accept the ADC credentials file.

@jpoehnelt yeah apologies that phrasing is a bit too ambitious. Really the goal here is just to alleviate the amount of Google Cloud setup required to get started. Given gcloud auth with some specified scopes works for APIs like Drive, Sheets, etc...feels like a win for folks at orgs that won't have permissions to create custom OAuth clients or Terraform perms up - but totally open to whatever pattern here makes the most sense!

@jpoehnelt jpoehnelt added area: auth cla: no This human has *not* signed the Contributor License Agreement. complexity: medium Moderate change, some review needed labels Mar 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: auth cla: no This human has *not* signed the Contributor License Agreement. complexity: medium Moderate change, some review needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants