stack-auth
Authentication strategies for CipherStash services.
All strategies implement the [AuthStrategy] trait, which provides a single
get_token method that returns a valid
[ServiceToken]. Token caching and refresh are handled automatically.
Strategies
| Strategy | Use case | Credentials |
|---|---|---|
[AutoStrategy] |
Recommended default — detects credentials automatically | CS_CLIENT_ACCESS_KEY + CS_WORKSPACE_CRN, or ~/.cipherstash/auth.json |
[AccessKeyStrategy] |
Service-to-service / CI | Static access key + workspace CRN |
[DeviceSessionStrategy] |
Long-lived sessions with refresh | OAuth token (from device code flow or disk) |
[DeviceCodeStrategy] |
CLI login (RFC 8628) | User authorizes in browser |
StaticTokenStrategy |
Tests only (test-utils feature) |
Pre-obtained token used as-is |
Quick start
For most applications, [AutoStrategy] is the simplest way to get started:
use stack_auth::AutoStrategy;
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let strategy = AutoStrategy::detect()?;
// That's it — get_token() handles the rest.
# Ok(())
# }
For service-to-service authentication with an access key:
use stack_auth::AccessKeyStrategy;
use cts_common::Crn;
# fn run() -> Result<(), Box<dyn std::error::Error>> {
let crn: Crn = "crn:ap-southeast-2.aws:ZVATKW3VHMFG27DY".parse()?;
let key = "CSAKkeyId.keySecret".parse()?;
let strategy = AccessKeyStrategy::new(crn, key)?;
# Ok(())
# }
Error handling
Every fallible operation returns [AuthError], a structured enum in which
each variant wraps a dedicated error struct. Errors are designed to tell the
developer exactly what to do next:
- Stable machine-readable codes — [
AuthError::error_code] returns aSCREAMING_CASEidentifier (e.g.NOT_AUTHENTICATED,WORKSPACE_MISMATCH) suitable for logs, metrics, and programmatic handling. The same taxonomy crosses the FFI boundary: the@cipherstash/authnpm package surfaces these codes as thetypediscriminant of its typedAuthFailureunion. - Actionable help — every variant implements
miette::Diagnostic, sohelp()(andurl()when present) carry remediation guidance, e.g.NOT_AUTHENTICATEDsaysLog in with `stash login`, or set `CS_CLIENT_ACCESS_KEY` for service-to-service auth.Applications that render errors through miette (like the Stash CLI) show this automatically. - Structured payload — variants carry typed fields rather than
pre-formatted strings; e.g. [
WorkspaceMismatch] exposesexpected_workspaceandtoken_workspaceso callers can act on the values, not parse a message.
use miette::Diagnostic;
use stack_auth::{AuthError, AutoStrategy};
fn report(err: &AuthError) {
eprintln!("[{}] {err}", err.error_code());
if let Some(help) = err.help() {
eprintln!(" help: {help}");
}
if let Some(url) = err.url() {
eprintln!(" more: {url}");
}
if let AuthError::WorkspaceMismatch(m) = err {
eprintln!(
" token belongs to {}, strategy expects {}",
m.token_workspace, m.expected_workspace
);
}
}
match AutoStrategy::detect() {
Ok(_strategy) => { /* authenticated */ }
Err(err) => report(&err),
}
Extensibility
stack-auth exposes two layers that can be plugged independently:
┌──────────────────────────────────────────────────┐
│ AuthStrategy ─ acquisition layer │
│ get_token() -> ServiceToken │
│ AccessKeyStrategy / DeviceSessionStrategy / AutoStrategy│
│ ── or ── │
│ AuthStrategyFn (closure → AuthStrategy) │
└────────────────────────┬─────────────────────────┘
│ uses
┌────────────────────────▼─────────────────────────┐
│ TokenStore ─ persistence layer │
│ load() / save() of Token │
│ InMemoryTokenStore / NoStore │
│ ── or ── │
│ TokenStoreFn (closures → TokenStore) │
└──────────────────────────────────────────────────┘
Use [TokenStoreFn] when you want stack-auth's own strategies to handle
HTTP/refresh, but you need to plug in custom persistence (a cookie,
a KV blob, Redis). Wire it via the strategy's builder.
Use [AuthStrategyFn] when you want to bring your own token acquisition
end-to-end — typically because the strategy lives across an FFI boundary
(e.g. a JS getToken() reached via protect-ffi). The closure runs every
time a token is needed.
Module paths mirror this split: stack_auth::auth groups the
acquisition layer, stack_auth::store groups the persistence
layer. All items are also re-exported at the crate root.
Security
Sensitive values ([SecretToken]) are automatically zeroized when dropped
and are masked in Debug output to prevent accidental
leaks in logs.
Token refresh
All strategies that cache tokens ([AccessKeyStrategy], [DeviceSessionStrategy],
[AutoStrategy]) share the same internal refresh engine. See the
[AuthStrategy] trait docs for a full description of the concurrency model
and flow diagram.