Google Cloud SDK for Rust

Latest Version tests and formatting security audit unsafe

Async gRPC clients for every Google Cloud API, generated from googleapis with Tonic, and the glue to authenticate them and your own services.

The library provides:

This is not an official Google project.

gcloud-sdk and the official Google Cloud Rust SDK

Google now publishes an official Google Cloud Rust SDK: high-level clients for most Google Cloud services and google-cloud-auth for credentials. Since 0.33 gcloud-sdk builds on it.

Authentication comes from Google

Every access token and ID token is minted by google-cloud-auth. Credential formats, new credential sources and security fixes come from Google, and gcloud-sdk no longer maintains a second copy of them. It also covers cases the old token sources of gcloud-sdk did not:

The REST clients are removed for the same reason: the official crates cover them, see REST APIs moved to the official SDK.

The gaps gcloud-sdk fills

The official SDK still leaves gaps, and closes them slowly. As of October 2026:

Opinionated integrations

gcloud-sdk makes choices the official SDK leaves to you:

Which one to use

The two combine in one application. The authentication of gcloud-sdk is google-cloud-auth, re-exported as gcloud_sdk::google_cloud_auth, so one Credentials serves the official clients and the gcloud-sdk clients.

Migrating from 0.32

0.33 is a breaking release: see MIGRATING.md for the API mapping, removed features, behaviour changes and the REST APIs that moved to the official crates.

Getting started

Features

Each Google API is behind its own feature, and you must enable the ones you use. For example, for Cloud Pub/Sub write features = ["google-pubsub-v1"] in Cargo.toml.

The feature name is the proto package name with periods replaced by hyphens: google.pubsub.v1 is google-pubsub-v1.

The full list is on docs.rs, and in the # Google API features section of gcloud-sdk/Cargo.toml.

The library features:

Example for gRPC

[dependencies]
gcloud-sdk = { version = "0.33", features = ["google-firestore-v1"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
use gcloud_sdk::google::firestore::v1::firestore_client::FirestoreClient;
use gcloud_sdk::google::firestore::v1::ListDocumentsRequest;
use gcloud_sdk::*;

let google_project_id = GoogleEnvironment::detect_google_project_id()
    .await
    .expect("No Google Project ID detected. Please specify it explicitly using env variable: PROJECT_ID");
let cloud_resource_prefix = format!("projects/{}/databases/(default)", google_project_id);

// The Application Default Credentials, for the cloud-platform scope
let firestore_client: GoogleApi<FirestoreClient<GoogleAuthMiddleware>> =
    GoogleApi::from_function(
        FirestoreClient::new,
        "https://firestore.googleapis.com",
        // cloud resource prefix: used only for some of the APIs (such as Firestore)
        Some(cloud_resource_prefix.clone()),
    )
    .await?;

let response = firestore_client
    .get()
    .list_documents(tonic::Request::new(ListDocumentsRequest {
        parent: format!("{}/documents", cloud_resource_prefix),
        ..Default::default()
    }))
    .await?;

Full example available here. More examples are located here.

Request timeouts

By default a call waits for as long as its connection lives. The channel's HTTP/2 keep-alive notices a server that stopped answering after up to two minutes: a ping every 60 s, and 60 s for its answer.

RequestTimeouts sets client-side limits for a call, both off by default:

use std::time::Duration;

// Every call of the client
let firestore_client = firestore_client
    .with_request_timeouts(RequestTimeouts::new().with_total(Duration::from_secs(30)));

// One call: replaces both limits of the client, and RequestTimeouts::new() switches them off
let mut request = tonic::Request::new(run_query_request);
request
    .extensions_mut()
    .insert(RequestTimeouts::new().with_idle(Duration::from_secs(30)));

A call that runs out of a limit fails with Code::DeadlineExceeded, and the source of its status is a RequestTimeoutElapsed.

Be aware not to set idle on a bidirectional stream such as Firestore's Listen: it stays quiet until there is something to send.

Crypto providers

google-cloud-auth signs service account JWTs with a rustls CryptoProvider. With the default features it uses aws-lc-rs.

With default-features = false, enable a TLS feature and install a provider yourself before building credentials, with the same rustls version as google-cloud-auth (0.23):

[dependencies]
gcloud-sdk = { version = "0.33", default-features = false, features = ["tls-roots", "google-firestore-v1"] }
rustls = { version = "0.23", features = ["ring"] }
rustls::crypto::ring::default_provider()
    .install_default()
    .expect("Failed to install rustls crypto provider");

Without a provider, the constructors that use the ADC return ErrorKind::CryptoProviderMissing when the ADC signs locally, with a service account key used directly or as the source of an impersonation: from_function, from_function_with_scopes and the other from_function* without credentials, GoogleAuthHeaders::from_adc, GoogleAuthHeaders::from_adc_with_scopes and GoogleAuthHeaders::id_token_from_adc.

For a service account key that is not the ADC, GoogleAuthHeaders::from_service_account_key runs the same check and returns the same error:

let key: serde_json::Value = serde_json::from_str(&std::fs::read_to_string("key.json")?)?;
let auth_headers = GoogleAuthHeaders::from_service_account_key(key, GCP_DEFAULT_SCOPES.clone()).await?;

Credentials you build yourself are not checked: the ones passed to from_function_with_credentials*, with_credentials*, GoogleAuthHeaders::from(..), and the source credentials of GoogleAuthHeaders::id_token_impersonating. Install the provider before building them, otherwise their refresh task panics.

The same call fixes this error, which appears when more than one rustls provider is compiled into your application:

no process-level CryptoProvider available -- call CryptoProvider::install_default() before this point

Google authentication

The library authenticates with google-cloud-auth. GoogleApi::from_function uses the Application Default Credentials (ADC) for the https://www.googleapis.com/auth/cloud-platform scope. ADC looks for credentials in the following places, preferring the first location found:

The JSON file can be a service account key, user credentials, impersonated service account credentials or a workload identity federation config.

Credentials must be built inside a Tokio runtime.

Custom scopes

from_function_with_scopes() takes the scopes instead of from_function():

let client: GoogleApi<FirestoreClient<GoogleAuthMiddleware>> =
    GoogleApi::from_function_with_scopes(
        FirestoreClient::new,
        "https://firestore.googleapis.com",
        Some(cloud_resource_prefix),
        vec!["https://www.googleapis.com/auth/datastore".to_string()],
    )
    .await?;

Custom credentials

from_function_with_credentials() takes any Credentials built with google-cloud-auth, for example a service account key that is not the ADC:

use gcloud_sdk::google_cloud_auth::credentials::service_account;

let credentials = service_account::Builder::new(serde_json::from_str(&service_account_key)?).build()?;

let client: GoogleApi<FirestoreClient<GoogleAuthMiddleware>> =
    GoogleApi::from_function_with_credentials(
        FirestoreClient::new,
        "https://firestore.googleapis.com",
        Some(cloud_resource_prefix),
        credentials,
    )
    .await?;

For tokens from your own source, implement google_cloud_auth::credentials::CredentialsProvider and wrap it with Credentials::from(..). The same Credentials work with the official Google Cloud Rust clients.

Workload identity federation

Workload identity federation gives "keyless" access from other clouds and CI systems. Point GOOGLE_APPLICATION_CREDENTIALS at the external_account config from gcloud iam workload-identity-pools create-cred-config. The supported sources:

Workforce identity pools and service account impersonation in the config work as well.

Local development

Don't confuse gcloud auth login with gcloud auth application-default login for local development, since the first authorizes only the gcloud tool to access the Cloud Platform.

The latter obtains user access credentials via a web flow and puts them in the well-known location for ADC. So to work for local development you need to use gcloud auth application-default login. gcloud auth application-default login --impersonate-service-account=... runs the code as a service account instead.

Routing headers

Some of the APIs (notably KMS, Artifact Registry and others) require additional headers such as x-goog-request-params. You can find an example how to handle it here

Service-to-service authentication

The library supports Google-signed ID tokens on both sides of a call between services:

Full examples: axum service, reqwest client. examples/id-token mints and verifies a token with the core API only, and is the end-to-end check of the keyless CI workflow.

Calling a Cloud Run or IAP service

GoogleAuthHeaders::id_token_from_adc mints ID tokens for one audience:

Google ID tokens are valid for one hour. google-cloud-auth caches the token and refreshes it before it expires.

gRPC (for example a tonic service on Cloud Run), with the default features:

use gcloud_sdk::{
    GoogleApi, GoogleAuthHeaders, GoogleAuthMiddleware, GoogleAuthMiddlewareLayer,
    IdTokenAudience,
};

let audience = IdTokenAudience::new("https://orders-abc123-ew.a.run.app");
let id_tokens = GoogleAuthHeaders::id_token_from_adc(&audience).await?;

let orders_client: GoogleApi<OrdersClient<GoogleAuthMiddleware>> =
    GoogleApi::from_function_with_middleware(
        OrdersClient::new,
        "https://orders-abc123-ew.a.run.app",
        GoogleAuthMiddlewareLayer::new(id_tokens, None)?,
    )
    .await?;

HTTP with reqwest-middleware feature:

gcloud-sdk = { version = "0.33", features = ["reqwest-middleware"] }
reqwest = "0.13"
reqwest-middleware = "0.5"
use gcloud_sdk::GoogleAuthReqwestMiddleware;

let client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
    .with(GoogleAuthReqwestMiddleware::new(id_tokens))
    .build();

let response = client
    .get("https://orders-abc123-ew.a.run.app/orders")
    .send()
    .await?;

Full example available here.

The middleware constructors take GoogleAuthHeaders or Arc<GoogleAuthHeaders>. For access tokens of the ADC instead of ID tokens, for example to call Google APIs over HTTP, use GoogleAuthHeaders::from_adc() or GoogleAuthHeaders::from_adc_with_scopes(..).

Without the reqwest-middleware feature, id_tokens.headers().await? gives the headers, authorization among them, for any HTTP client.

Which credentials work

GoogleAuthHeaders::id_token_from_adc mints with the identity of the ADC:

These credentials hold no service account and cannot mint an ID token for an audience, so id_token_from_adc returns ErrorKind::IdTokenNeedsImpersonation for them:

Use GoogleAuthHeaders::id_token_impersonating with any credentials but the last to mint ID tokens of a service account through IAM Credentials API:

use gcloud_sdk::google_cloud_auth::credentials::Builder as CredentialsBuilder;
use gcloud_sdk::{GoogleAuthHeaders, IdTokenAudience, ServiceAccountEmail};

let id_tokens = GoogleAuthHeaders::id_token_impersonating(
    &IdTokenAudience::new("https://orders-abc123-ew.a.run.app"),
    &ServiceAccountEmail::new("invoker@my-project.iam.gserviceaccount.com"),
    CredentialsBuilder::default().build()?,
)
.await?;

The caller needs roles/iam.serviceAccountOpenIdTokenCreator on that service account. This is also the way for local development, using your own gcloud auth application-default login credentials.

For a key file or a metadata server that is not the ADC, build the ID token credentials with google_cloud_auth::credentials::idtoken and pass them to GoogleAuthHeaders::from(..).

Receiving and verifying tokens

IdTokenVerifier from id-token-verify feature checks:

The keys are cached for the max-age of the response. A token signed with an unknown key ID refetches them, at most once every 30 seconds. When a fetch fails, the cached keys stay in use until their max-age passes.

gcloud-sdk = { version = "0.33", features = ["id-token-verify"] }

The verifier fetches the keys over HTTPS, so it needs tls-roots or tls-webpki-roots, or a TLS feature of reqwest in your application. Without one, every key fetch fails at runtime with KeysUnavailable.

use gcloud_sdk::id_token_verify::{IdTokenVerifier, IdTokenVerifyError};
use gcloud_sdk::IdTokenAudience;

let verifier = IdTokenVerifier::new(IdTokenAudience::new("https://orders-abc123-ew.a.run.app"))?;

match verifier.verify(bearer_token).await {
    Ok(verified) => {
        // Your own allowlist of callers
        let caller = verified.verified_email();
    }
    // Answer 401
    Err(IdTokenVerifyError::InvalidToken(reason)) => {}
    // Answer 503: the token may be valid, but Google keys are not available
    Err(IdTokenVerifyError::KeysUnavailable(error)) => {}
}

IdTokenVerifier::with_keys_source takes the keys from your own IdTokenKeysSource instead, for example a locally generated RSA key in tests.

The verifier checks signatures with the JWT crypto provider of the library features.

Protecting an axum service

VerifyIdTokenLayer from axum feature verifies the bearer token of every request:

Handlers receive the verified claims as VerifiedIdToken:

gcloud-sdk = { version = "0.33", features = ["id-token-verify", "axum"] }
axum = "0.8"
use std::sync::Arc;

use axum::routing::get;
use axum::Router;
use gcloud_sdk::id_token_verify::{
    IdTokenVerifier, PrincipalEmail, VerifiedIdToken, VerifyIdTokenLayer,
};
use gcloud_sdk::IdTokenAudience;

let verifier = Arc::new(IdTokenVerifier::new(IdTokenAudience::new(
    "https://orders-abc123-ew.a.run.app",
))?);
let billing = PrincipalEmail::new("billing@my-project.iam.gserviceaccount.com");

let app = Router::new()
    .route("/orders", get(list_orders))
    .layer(
        VerifyIdTokenLayer::new(verifier)
            .authorize(move |token| token.verified_email() == Some(&billing)),
    );

async fn list_orders(caller: VerifiedIdToken) -> String {
    format!("Orders for {:?}", caller.verified_email())
}

The layer is a tower layer, so it works the same way for tonic servers.

Full example available here.

JWT crypto provider

IdTokenVerifier verifies JWTs with jsonwebtoken, which needs a crypto provider. The library has a feature for each:

With default-features = false and id-token-verify, enable one of them, otherwise the build fails. With both enabled, aws-lc-rs is used. The library installs the provider of the enabled feature as the process default of jsonwebtoken, unless your application installed one before.

These features choose the crypto of the verifier only. auth-default-crypto and tls-webpki-roots build aws-lc-rs for google-cloud-auth and rustls anyway.

High-level APIs

Sometimes using proto generated APIs are tedious and cumbersome, so you may need to introduce facade APIs on top of them:

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Authors

The library was started as a fork of mechiru/googapis and mechiru/gouth libraries, but now includes much more: