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:
tls-roots (native-tls) and tls-webpki-roots (rustls) options for the connections.This is not an official Google project.
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.
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:
x-goog-user-project, GOOGLE_CLOUD_QUOTA_PROJECT) and universe domains;The REST clients are removed for the same reason: the official crates cover them, see REST APIs moved to the official SDK.
The official SDK still leaves gaps, and closes them slowly. As of October 2026:
Listen) and Datastore are not published as official crates, Spanner is pre-1.0, and BigQuery Storage Read sits behind an unstable cfg.Listen, Bigtable ReadRows, Speech v1 StreamingRecognize, and any streaming method of an API that has no official crate, or no gRPC transport in its official crate.tls-roots, gcloud-sdk uses native-tls for the gRPC channels and for the token requests of google-cloud-auth. Be aware that if another dependency enables the http3 feature of reqwest, the default reqwest backend is rustls again, and the token requests use it.gcloud-sdk makes choices the official SDK leaves to you:
VerifyIdTokenLayer verifies Google ID tokens in axum and tonic servers, with typed claims and an authorize check;GoogleAuthMiddlewareLayer and GoogleAuthReqwestMiddleware attach credentials to tonic and reqwest calls;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.
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.
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:
tls-roots: default, native-tls with the system roots;tls-webpki-roots: rustls with the webpki roots;auth-default-crypto: default, the aws-lc-rs crypto of google-cloud-auth;jwt-aws-lc-rs (default), jwt-rust-crypto and jwt-custom-provider: the crypto of the ID token verifier, see JWT crypto provider;id-token-verify, axum, reqwest-middleware: service-to-service authentication.[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.
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:
total: the whole call, authentication included. It is sent to the server as grpc-timeout, and suits unary calls;idle: the wait for the response to start and for each next part of the response, counted only while you are waiting for one. It suits server streams.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.
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
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:
GOOGLE_APPLICATION_CREDENTIALS environment variable;gcloud auth application-default login writes;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.
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?;
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 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:
AWS_ACCESS_KEY_ID environment keys, else EC2 IMDS (IMDSv2 when the config has a session token URL). ECS task roles, ~/.aws profiles and SSO are not supported;GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES=1.Workforce identity pools and service account impersonation in the config work as well.
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.
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
The library supports Google-signed ID tokens on both sides of a call between services:
id-token-verify feature);axum feature) and calling it with reqwest (reqwest-middleware feature).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.
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.
GoogleAuthHeaders::id_token_from_adc mints with the identity of the ADC:
gcloud auth application-default login --impersonate-service-account=... and workload identity federation with service account impersonation: as the impersonated service account.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:
gcloud auth application-default login (AuthorizedUser);ExternalAccountAuthorizedUser);ExternalAccount);GdchServiceAccount).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(..).
IdTokenVerifier from id-token-verify feature checks:
https://www.googleapis.com/oauth2/v3/certs;iss is Google, and aud is the audience of your service;exp, nbf and iat, with 30 seconds of leeway.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.
VerifyIdTokenLayer from axum feature verifies the bearer token of every request:
authorize check refuses the caller;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.
IdTokenVerifier verifies JWTs with jsonwebtoken, which needs a crypto provider. The library has a feature for each:
jwt-aws-lc-rs: default feature, uses aws-lc-rs, which is built from C sources and needs a C compiler;jwt-rust-crypto: the pure Rust alternative, uses the crates of RustCrypto;jwt-custom-provider: your application installs its own jsonwebtoken provider with CryptoProvider::install_default before it verifies a token.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.
Sometimes using proto generated APIs are tedious and cumbersome, so you may need to introduce facade APIs on top of them:
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
The library was started as a fork of mechiru/googapis and mechiru/gouth libraries, but now includes much more: