Skip to content

feat(auth): add support for headless workforce identity#4825

Merged
alvarowolfx merged 7 commits intogoogleapis:mainfrom
alvarowolfx:impl-auth-workforce-id
Mar 3, 2026
Merged

feat(auth): add support for headless workforce identity#4825
alvarowolfx merged 7 commits intogoogleapis:mainfrom
alvarowolfx:impl-auth-workforce-id

Conversation

@alvarowolfx
Copy link
Copy Markdown
Collaborator

Towards #4821

@alvarowolfx alvarowolfx changed the title feat(auth): Add support for headless workforce identity feat(auth): add support for headless workforce identity Feb 25, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 25, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.53%. Comparing base (a50ea91) to head (bb360d7).
⚠️ Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
src/auth/src/credentials/external_account.rs 75.00% 9 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4825      +/-   ##
==========================================
- Coverage   94.61%   94.53%   -0.08%     
==========================================
  Files         208      208              
  Lines        8164     8198      +34     
==========================================
+ Hits         7724     7750      +26     
- Misses        440      448       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alvarowolfx alvarowolfx marked this pull request as ready for review March 2, 2026 21:02
@alvarowolfx alvarowolfx requested review from a team as code owners March 2, 2026 21:02
@alvarowolfx
Copy link
Copy Markdown
Collaborator Author

Tested following internal guide on Workforce Identity and with Access Boundaries.

code:

use google_cloud_auth::credentials::{Builder, CacheableResource};
use google_cloud_storage::client::StorageControl;
use httptest::http::Extensions;
use scoped_env::ScopedEnv;

const BUCKET_ID: &str = "REDACTED";

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Enable trust boundaries
    let _env = ScopedEnv::set("GOOGLE_AUTH_ENABLE_TRUST_BOUNDARIES", "true");

    println!("Building Credentials...");
    // Will use ADC with the environment variable set
    let creds = Builder::default().build()?;
    println!("Credentials built: {:?}", creds);

    for _ in 0..10 {
        let cached_headers = creds.headers(Extensions::new()).await?;
        let headers = match cached_headers {
            CacheableResource::New { data, .. } => data,
            CacheableResource::NotModified => unreachable!("should always get new headers"),
        };
        let token = headers.get("Authorization");
        println!("Token: {:?}", token);
        let locations = headers.get("x-goog-allowed-locations");
        println!("Locations: {:?}", locations);
        if locations.is_some() {
            println!("Locations found, breaking");
            break;
        }
        tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
    }

    let client = StorageControl::builder()
        .with_credentials(creds.clone())
        .build()
        .await?;
    let bucket = client
        .get_bucket()
        .set_name(format!("projects/_/buckets/{BUCKET_ID}"))
        .send()
        .await?;
    println!("successfully obtained bucket metadata {bucket:?}");

    Ok(())
}

output:

Building Credentials...
Credentials built: Credentials { inner: AccessTokenCredentials { inner: CredentialsWithAccessBoundary { credentials: ExternalAccountCredentials { token_provider: TokenCache { rx_token: Receiver { shared: Shared { value: RwLock(PhantomData<std::sync::poison::rwlock::RwLock<core::option::Option<core::result::Result<(google_cloud_auth::token::Token, google_cloud_auth::credentials::EntityTag), google_cloud_gax::error::credentials::CredentialsError>>>>, RwLock { data: None }), version: Version(0), is_closed: false, ref_count_rx: 1 }, version: Version(0) } }, quota_project_id: None }, access_boundary: AccessBoundary { rx_header: Receiver { shared: Shared { value: RwLock(PhantomData<std::sync::poison::rwlock::RwLock<core::option::Option<google_cloud_auth::access_boundary::BoundaryValue>>>, RwLock { data: None }), version: Version(0), is_closed: false, ref_count_rx: 1 }, version: Version(0) } } } } }
Token: Some(Sensitive)
Locations: None
Token: Some(Sensitive)
Locations: Some("0x80000000000")
Locations found, breaking
successfully obtained bucket metadata Bucket { name: "projects/_/buckets/REDACTED", bucket_id: "wf-demo", etag: "CAI=", project: "projects/307586025878", metageneration: 2, location: "US", location_type: "multi-region", storage_class: "STANDARD", rpo: "DEFAULT", acl: [], default_object_acl: [], lifecycle: None, create_time: Some(Timestamp { seconds: 1653524454, nanos: 427000000 }), cors: [], update_time: Some(Timestamp { seconds: 1772483935, nanos: 587000000 }), default_event_based_hold: false, labels: {}, website: None, versioning: None, logging: None, owner: None, encryption: None, billing: None, retention_policy: None, iam_config: Some(IamConfig { uniform_bucket_level_access: Some(UniformBucketLevelAccess { enabled: true, lock_time: Some(Timestamp { seconds: 1661300454, nanos: 427000000 }) }), public_access_prevention: "inherited" }), satisfies_pzs: false, custom_placement_config: None, autoclass: None, hierarchical_namespace: None, soft_delete_policy: Some(SoftDeletePolicy { retention_duration: Some(Duration { seconds: 604800, nanos: 0 }), effective_time: Some(Timestamp { seconds: 1709280000, nanos: 0 }) }), object_retention: None, ip_filter: None }

Copy link
Copy Markdown
Collaborator

@coryan coryan left a comment

Choose a reason for hiding this comment

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

A few ideas to improve clarity.

@alvarowolfx alvarowolfx merged commit 9216ea7 into googleapis:main Mar 3, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants