Skip to main content

Getting Started

Five minutes from an empty project to a signed AWS request. The same shape works for every provider — only the entry point differs.

1. Install​

Add the reqsign facade with the feature for each provider you need (features are additive):

cargo add reqsign --features aws
cargo add tokio --features full
cargo add http anyhow
FeatureEnables
awsAWS SigV4 (alias of aws-v4)
aws-v4aAWS SigV4a multi-region signing
azureAzure Storage
googleGoogle Cloud
google-credential-access-boundary-client-sideClient-side CAB downscoping (implies google)
aliyunAliyun OSS
huaweicloudHuawei Cloud OBS
oracleOracle Cloud
tencentTencent COS
volcengineVolcengine TOS
fullEverything above

The default default-context feature wires a ready-to-use runtime (Tokio file reading, reqwest HTTP, Tokio command execution). Keep it unless you are bringing your own runtime. Requires Rust 1.86.0+.

2. Sign your first request​

This is the repository's compiled example — CI builds it on every commit:

use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
use reqsign::aws;
// Create a default signer for S3 in us-east-1
let signer = aws::default_signer("s3", "us-east-1");

// Build a request
let mut req = http::Request::builder()
.method(http::Method::GET)
.uri("https://s3.amazonaws.com/my-bucket/my-object")
.body(())
.unwrap()
.into_parts()
.0;

// Sign the request
signer.sign(&mut req, None).await?;


// Execute the request would require rebuilding with body
// In real usage, you'd use your HTTP client here
println!("Request signed successfully!");

Ok(())
}

Three things happen here:

  1. aws::default_signer("s3", "us-east-1") composes the default runtime context with AWS's full credential chain — environment variables, shared profiles, SSO, ECS, IMDS, and more. See Loading credentials.
  2. The request is built with the plain http crate. Reqsign signs http::request::Parts directly — there is no wrapper client to adopt.
  3. signer.sign(&mut req, None).await? mutates the request head in place, adding the Authorization header and companions. Passing Some(duration) instead selects query authentication where the provider supports it — see Presigning.

3. Run it​

AWS_ACCESS_KEY_ID=AKIDEXAMPLE \
AWS_SECRET_ACCESS_KEY=example-secret \
cargo run

The example signs the request and prints a confirmation; sending it is your HTTP client's job. Any placeholder credentials produce a structurally valid signature — the service rejects them, but the wire format is real.

Every provider, one pattern​

Swap the entry point and the rest of the code stays the same. Each entry composes that provider's own credential chain — the provider pages document exactly which sources:

ProviderEntry
AWS SigV4aws::default_signer(service, region)
AWS SigV4aaws::v4a::default_signer(service, region_set)
Azure Storageazure::default_signer()
Google Cloudgoogle::default_signer(service)
Aliyun OSSaliyun::default_signer(bucket)
Huawei Cloud OBShuaweicloud::default_signer(bucket)
Oracle Cloudoracle::default_signer()
Tencent COStencent::default_signer()
Volcengine TOSvolcengine::default_signer(region)

A default signer is an ordinary Signer: replace any component and keep the rest —

async fn main() -> anyhow::Result<()> {
use reqsign::aws::{self, StaticCredentialProvider};
let signer = aws::default_signer("s3", "us-east-1").with_credential_provider(
StaticCredentialProvider::new("AKIDEXAMPLE", "example-secret-key"),
);

let provider = reqsign::aws::DefaultCredentialProvider::new().push_front(
StaticCredentialProvider::new("AKIDEXAMPLE", "example-secret-key"),
);
let _custom_chain_signer =
aws::default_signer("s3", "us-east-1").with_credential_provider(provider);

let mut req = http::Request::get("https://s3.amazonaws.com/my-bucket/my-object")
.body(())?
.into_parts()
.0;
signer.sign(&mut req, None).await?;
assert!(req.headers.contains_key("authorization"));
Ok(())
}

Where to go next​