Skip to main content

Scoped credential granting

Granting exchanges a broad credential for a narrower one before any request is signed: scoped to a bucket, a prefix, a permission, a time window. Vendor SDKs rarely give these operations a common shape; Reqsign's Granter does, while keeping each provider's semantics explicit.

The operations​

ProviderOperationScopes to
AWS SigV4S3 Access Grants (GetDataAccess)A registered grant: location, prefix, permission
AWS SigV4S3 Express Session (CreateSession)One directory bucket, read-only or read-write
Azure StorageUser delegation SASContainer/blob, permissions, validity window
Google CloudCredential Access Boundary (server-side)Buckets/prefixes via an STS exchange
Google CloudCredential Access Boundary (client-side)Same, derived locally without an STS round-trip

Each operation is listed with source references on its provider page.

Walkthrough: S3 Express session credentials​

The same dependencies as Getting Started suffice. This example uses the default context for environment, file, and HTTP access:

use reqsign::aws::{
DefaultCredentialProvider, S3ExpressSessionConfig, S3ExpressSessionGrant,
S3ExpressSessionGranter, S3ExpressSessionMode,
};
use reqsign::{Granter, default_context};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = S3ExpressSessionConfig::from_bucket("my-bucket--usw2-az1--x-s3", "us-west-2")?;
let grant = S3ExpressSessionGrant::new(S3ExpressSessionMode::ReadOnly);
let granter = Granter::new(
default_context(),
DefaultCredentialProvider::new(),
S3ExpressSessionGranter::new(config, grant),
);
let scoped = granter.grant(None).await?;
// Keep the entire credential, including expires_in, when passing it on.
assert!(scoped.expires_in.is_some());
Ok(())
}

Signing with automatically refreshed sessions​

For a long-lived signer, use S3ExpressSessionProvider. It performs the same CreateSession exchange when credentials are needed, preserves their expiration, and lets Signer refresh the session. S3 Express uses the s3express signing service and the directory bucket's zonal endpoint:

use reqsign::aws::{
self, DefaultCredentialProvider, S3ExpressSessionGrant, S3ExpressSessionMode,
S3ExpressSessionProvider,
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let provider = S3ExpressSessionProvider::new(
"my-bucket--usw2-az1--x-s3",
DefaultCredentialProvider::new(),
)
.with_region("us-west-2")
.with_grant(S3ExpressSessionGrant::new(S3ExpressSessionMode::ReadOnly));
let signer = aws::default_signer("s3express", "us-west-2").with_credential_provider(provider);
let mut req = http::Request::get(
"https://my-bucket--usw2-az1--x-s3.s3express-usw2-az1.us-west-2.amazonaws.com/object",
)
.body(())?
.into_parts()
.0;
// The signer caches the session with its expiration and refreshes as needed.
signer.sign(&mut req, None).await?;
Ok(())
}

When passing a manually granted credential to another component, retain the entire credential, including expires_in. Reconstructing it with AWS's StaticCredentialProvider loses the expiration and automatic refresh behavior. Keep granted keys and session tokens out of logs.

Semantics to rely on​

  • The source credential is cached and revalidated per grant; the granted result owns independent material and never aliases the cache.
  • expires_in requests a validity window where the operation supports one; the operation's own maximum applies.
  • Errors returned by the configured source or grant operation propagate without retry or reuse of a stale cached source. A source configured as a credential chain can still fall back between its providers.

Credential fields and types are provider-specific — see docs.rs for the exact API.