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
| Provider | Operation | Scopes to |
|---|---|---|
| AWS SigV4 | S3 Access Grants (GetDataAccess) | A registered grant: location, prefix, permission |
| AWS SigV4 | S3 Express Session (CreateSession) | One directory bucket, read-only or read-write |
| Azure Storage | User delegation SAS | Container/blob, permissions, validity window |
| Google Cloud | Credential Access Boundary (server-side) | Buckets/prefixes via an STS exchange |
| Google Cloud | Credential 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_inrequests 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.