Files
mcias/clients/rust
Kyle Isom 39d9ffb79a Add service-context login policy enforcement
Services send service_name and tags in POST /v1/auth/login.
MCIAS evaluates auth:login policy with these as the resource
context after credentials are verified, enabling rules like:
  deny guest/viewer human accounts from env:restricted services
  deny guest accounts from specific named services

- loginRequest: add ServiceName and Tags fields
- handleLogin: evaluate policy after credential+TOTP check;
  policy deny returns 403 (not 401) to distinguish access
  restriction from bad credentials
- Go client: Options.ServiceName/Tags stored on Client,
  sent automatically in every Login() call
- Python client: service_name/tags on __init__, sent in login()
- Rust client: ClientOptions.service_name/tags, LoginRequest
  fields, Client stores and sends them in login()
- openapi.yaml: document service_name/tags request fields
  and 403 response for policy-denied logins
- engineering-standards.md: document service_name/tags in
  [mcias] config section with policy examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 21:11:35 -07:00
..

mcias-client (Rust)

Async Rust client library for the MCIAS identity and access management API.

Requirements

  • Rust 2021 edition (stable toolchain)
  • Tokio async runtime

Installation

Add to Cargo.toml:

[dependencies]
mcias-client = { path = "path/to/clients/rust" }

Quick Start

use mcias_client::{Client, ClientOptions};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new(
        "https://auth.example.com".to_string(),
        ClientOptions::default(),
    )?;

    // Authenticate.
    let (token, expires_at) = client.login("alice", "s3cret", None).await?;
    println!("token expires at {expires_at}");

    // The token is stored in the client automatically.
    let accounts = client.list_accounts().await?;

    // Revoke the token when done.
    client.logout().await?;

    Ok(())
}

Custom CA Certificate

let ca_pem = std::fs::read("/etc/mcias/ca.pem")?;
let client = Client::new(
    "https://auth.example.com".to_string(),
    ClientOptions {
        ca_cert_pem: Some(ca_pem),
        token: None,
    },
)?;

Error Handling

All methods return Result<_, MciasError>:

use mcias_client::MciasError;

match client.login("alice", "wrongpass", None).await {
    Err(MciasError::Auth { message }) => eprintln!("auth failed: {message}"),
    Err(MciasError::Forbidden { message }) => eprintln!("forbidden: {message}"),
    Err(MciasError::NotFound { message }) => eprintln!("not found: {message}"),
    Err(MciasError::InvalidInput { message }) => eprintln!("bad input: {message}"),
    Err(MciasError::Conflict { message }) => eprintln!("conflict: {message}"),
    Err(MciasError::Server { status, message }) => eprintln!("server error {status}: {message}"),
    Err(MciasError::Transport(e)) => eprintln!("network error: {e}"),
    Err(MciasError::Decode(e)) => eprintln!("parse error: {e}"),
    Ok((token, _)) => println!("ok: {token}"),
}

Thread Safety

Client is Send + Sync. The internal token is wrapped in Arc<RwLock<Option<String>>> for safe concurrent access.

Running Tests

cargo test
cargo clippy -- -D warnings