laravel-iam (Rust SDK)
A thin, fail-closed Rust client for the Laravel IAM authorization server. It asks the control plane for policy decisions and verifies OIDC tokens — and it is built so that a gate cannot accidentally open.
Same wire contract as the production PHP client (
Padosoft\Iam\Client), different language. No policy logic lives on the client: every decision belongs to the server.
Why
Authorization clients fail in the worst possible way when they fail open — a timeout or a 500 quietly becomes "allow". This SDK makes that impossible by construction:
- A network error, timeout, 5xx, 4xx, malformed body or unverifiable token always maps to deny.
- There is no fail-open switch. (If you need to tolerate an outage, do it deliberately at the application layer — never silently in the transport.)
- An
alloweddecision that stillrequires_step_upis treated as not yet allowed.
Install
[]
= "1"
= { = "1", = ["rt-multi-thread", "macros"] }
Enable the synchronous client with the blocking feature:
= { = "1", = ["blocking"] }
Quick start
use Duration;
use ;
use json;
async
The fail-closed note (read this)
check() returns Result<Decision, IamError>. The [ResultExt::is_allowed] helper is the safe
gate: it is true only when the call succeeded and the decision is truly granted (allowed
with no pending step-up). Every other outcome — including every error variant — is false.
# use ResultExt;
# async
If you need the details (auditing, step-up flows), inspect the Ok(Decision):
# use ;
#
Token verification
verify_token validates an ES256 signature against the server's JWKS
({base_url}/.well-known/jwks.json, cached) plus the iss / aud / exp / nbf claims. Configure
the expected issuer and audience on the builder:
# use Error;
#
# async
API
| Method | Description |
|---|---|
IamClient::builder() |
base_url, token, timeout (default 2s), issuer, audience → build() / build_blocking() |
check(DecisionQuery) -> Result<Decision, IamError> |
POST {base_url}/decisions/check |
list_resources(Subject, relation) -> Result<Vec<Resource>, IamError> |
POST {base_url}/decisions/list-resources |
verify_token(jwt) -> Result<Claims, IamError> |
ES256 + iss/aud/exp against the cached JWKS |
Decision::granted() / is_allowed() |
allowed and no pending step-up |
Result::is_allowed() (via ResultExt) |
the fail-closed gate: any error ⇒ false |
Errors
All variants of IamError (Network, Timeout, Unauthorized, Http, Malformed,
TokenInvalid, Config) mean "could not obtain a permit" and must be treated as deny — which
ResultExt::is_allowed does for you.
Wire contract
This client mirrors the PHP HttpDecider exactly:
- Endpoint:
POST {base_url}/decisions/check - Headers:
Accept: application/json,Authorization: Bearer <service token> - Request body:
{ subject: {type, id}, permission, organization, application, resource, context, current_aal, explain } - Response:
{ allowed, decision_id, policy_version, requires_step_up, required_aal, explanation }, parsed defensively (any wrong-typed field falls back to its safe default; a non-object body is a deny).
License
MIT © Padosoft. See LICENSE.