rs-ali-sts 0.1.2

Alibaba Cloud STS (Security Token Service) SDK for Rust
Documentation

rs-ali-sts

Crates.io Documentation License: MIT Build Status

中文文档

Alibaba Cloud STS (Security Token Service) SDK for Rust.

Provides both async and sync (blocking) clients covering all 4 STS API operations:

  • assume_role — Assume a RAM role to obtain temporary security credentials
  • assume_role_with_saml — SAML-based SSO role assumption
  • assume_role_with_oidc — OIDC-based SSO role assumption
  • get_caller_identity — Query the identity of the current caller

Features

  • Async and Blocking — Choose between async (Client) or sync (blocking::Client)
  • Builder Pattern — Ergonomic request construction with try_build() for fallible builds
  • Credential Chain — Automatic credential resolution from environment or profile files
  • Clock Skew Correction — Automatic adjustment for local clock drift
  • Concurrent Request Limiting — Built-in semaphore for async client
  • Security First — Credentials redacted in debug output, HTTPS POST, rustls TLS

Requirements

  • Rust 1.93+ (edition 2024)

Installation

Add to your Cargo.toml:

[dependencies]
rs-ali-sts = "0.1.2"

# For async usage, add a tokio runtime:
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

To use the synchronous (blocking) client:

[dependencies]
rs-ali-sts = { version = "0.1", features = ["blocking"] }

Quick Start

Async (recommended)

use rs_ali_sts::{Client, Credential, AssumeRoleRequest};

#[tokio::main]
async fn main() -> rs_ali_sts::Result<()> {
    // Create client with credential
    let client = Client::new(Credential::new("your-access-key-id", "your-access-key-secret"))?;

    // Build request using builder pattern
    let request = AssumeRoleRequest::builder()
        .role_arn("acs:ram::123456:role/example-role")
        .role_session_name("my-session")
        .duration_seconds(3600)
        .build();  // or .try_build()? for fallible version

    let resp = client.assume_role(request).await?;

    println!("Temporary AccessKeyId: {}", resp.credentials.access_key_id);
    println!("Expiration: {}", resp.credentials.expiration);
    Ok(())
}

Blocking (sync)

use rs_ali_sts::blocking::Client;
use rs_ali_sts::{Credential, AssumeRoleRequest};

fn main() -> rs_ali_sts::Result<()> {
    let client = Client::new(Credential::new("your-access-key-id", "your-access-key-secret"))?;

    let request = AssumeRoleRequest::builder()
        .role_arn("acs:ram::123456:role/example-role")
        .role_session_name("my-session")
        .build();

    let resp = client.assume_role(request)?;

    println!("Temporary AccessKeyId: {}", resp.credentials.access_key_id);
    Ok(())
}

Credential Resolution

The SDK supports multiple ways to provide credentials. Client::from_env() tries them in order:

1. Explicit credential

let client = Client::new(Credential::new("LTAI5t...", "your-secret"))?;

2. Environment variables

export ALIBABA_CLOUD_ACCESS_KEY_ID=LTAI5t...
export ALIBABA_CLOUD_ACCESS_KEY_SECRET=your-secret
let client = Client::from_env()?;  // Reads from environment

3. Profile file (~/.alibabacloud/credentials)

[default]
access_key_id = LTAI5t...
access_key_secret = your-secret

[production]
access_key_id = LTAI5tprod...
access_key_secret = prod-secret
// Uses default chain: Environment -> Profile (default)
let client = Client::from_env()?;

Builder Pattern

All request types support the builder pattern with two build methods:

// build() - panics if required fields are missing
let request = AssumeRoleRequest::builder()
    .role_arn("acs:ram::123456:role/example")
    .role_session_name("session")
    .build();

// try_build() - returns Result, useful for dynamic input
let request = AssumeRoleRequest::builder()
    .role_arn(user_input_arn)
    .role_session_name(session_name)
    .try_build()?;  // Returns Err if fields are missing

API Reference

AssumeRole

let request = AssumeRoleRequest::builder()
    .role_arn("acs:ram::123456:role/my-role")
    .role_session_name("session-name")
    .policy("{\"Version\":\"1\",\"Statement\":[...]}")  // Optional
    .duration_seconds(3600)                              // Optional: 900-43200
    .external_id("external-id")                          // Optional: cross-account
    .build();

let resp = client.assume_role(request).await?;
// resp.credentials.access_key_id
// resp.credentials.access_key_secret
// resp.credentials.security_token
// resp.credentials.expiration

AssumeRoleWithSAML

let request = AssumeRoleWithSamlRequest::builder()
    .saml_provider_arn("acs:ram::123456:saml-provider/my-idp")
    .role_arn("acs:ram::123456:role/saml-role")
    .saml_assertion("base64-encoded-assertion")
    .build();

let resp = client.assume_role_with_saml(request).await?;

AssumeRoleWithOIDC

let request = AssumeRoleWithOidcRequest::builder()
    .oidc_provider_arn("acs:ram::123456:oidc-provider/my-oidc")
    .role_arn("acs:ram::123456:role/oidc-role")
    .oidc_token("eyJhbGciOi...")
    .role_session_name("oidc-session")  // Optional
    .build();

let resp = client.assume_role_with_oidc(request).await?;

GetCallerIdentity

let resp = client.get_caller_identity().await?;
println!("Account ID: {}", resp.account_id);
println!("Identity ARN: {}", resp.arn);

Configuration

use std::time::Duration;
use rs_ali_sts::{Client, ClientConfig, Credential, SignatureVersion};

let config = ClientConfig::default()
    .with_endpoint("https://sts-vpc.cn-hangzhou.aliyuncs.com")  // VPC endpoint
    .with_timeout(Duration::from_secs(60))
    .with_connect_timeout(Duration::from_secs(10))
    .with_max_concurrent_requests(20)
    .with_signature_version(SignatureVersion::V2_0);

let client = Client::with_config(Credential::new("id", "secret")?, config)?;

Error Handling

use rs_ali_sts::StsError;

match client.assume_role(request).await {
    Ok(resp) => println!("Success: {}", resp.credentials.access_key_id),
    Err(StsError::Api { request_id, code, message, .. }) => {
        eprintln!("API error [{}]: {} (RequestId: {})", code, message, request_id);
    }
    Err(StsError::Validation(msg)) => eprintln!("Validation error: {}", msg),
    Err(StsError::Credential(msg)) => eprintln!("Credential error: {}", msg),
    Err(e) => eprintln!("Error: {}", e),
}
Error Variant Description
HttpClient Network/connection error
Http Non-JSON HTTP response
Api Alibaba Cloud API error (with request_id, code)
Validation Request validation error
Credential Credential resolution failure
Signature Signature computation error
Config Configuration error

Security Features

Feature Description
Credential Redaction access_key_secret and security_token shown as **** in debug output
HTTPS POST Credentials never appear in URLs
rustls TLS Pure Rust TLS, no OpenSSL dependency
UUID v4 Nonce Prevents replay attacks
HMAC-SHA1 Signature algorithm (compatible with Alibaba Cloud STS)
File Permission Check Warns on insecure credential file permissions (Unix)

License

Licensed under the MIT License.