Skip to main content

Crate assay_auth

Crate assay_auth 

Source
Expand description

Auth layer for assay-engine — a self-hosted, single-binary Ory replacement for assay-engine v0.2.0.

assay-auth packages every primitive a serious identity provider needs into one crate that composes into crate::AuthCtx and is mounted under /auth by the engine:

ModuleReplacesPurpose
sessionOry Kratos (sessions)Cookie + CSRF session manager (Argon2id-backed)
passwordOry Kratos (passwords)Argon2id PHC strings, peppered hashing
jwtHydra (JWT)RS256 issue/verify with rotated JWKS
oidcKratos (federation)OIDC client — log in via Google/Apple/GitHub/upstream
oidc_providerOry HydraFull OIDC provider/authorize, /token, /userinfo, /.well-known/*, RFC 7009 revoke, RFC 7662 introspect, back-channel logout
passkeyKratos (WebAuthn)webauthn-rs-backed passkey register + auth ceremonies
zanzibarOry Keto / SpiceDBReBAC tuples + recursive-CTE walk on PG18 + SQLite
biscuit(Ory has nothing)Datalog-attenuable capability tokens — always-on
storeUserStore / SessionStore traits + PG / SQLite backends
adminOry Console (HTTP API)Cross-cutting admin endpoints (users, sessions, Zanzibar, …)

§Why use assay-auth instead of Ory?

  • One static binary (assay-engine, ~9 MB stripped) replaces a stack of Kratos + Hydra + Keto + Oathkeeper containers. Same features, ~50× less RAM and one process to ship/log/restart.
  • Backend symmetry. PG18 + SQLite are both first-class via feature flags. SQLite means a self-hosted single-tenant deployment needs no database server at all — unique vs Ory.
  • Biscuit out of the box. Datalog-attenuable capability tokens that callers can scope down further without a server round-trip. Ory has nothing equivalent; this is a real differentiator.
  • Workflow + auth share storage. Atomic transactions across auth.usersworkflow.workflows (cross-schema FKs on PG, both attachments on SQLite) — signups can mint workflow records in one transaction. Splitting Ory + Temporal forces 2-phase commit.
  • Lua-scriptable. Every auth surface is reachable from the assay.auth Lua stdlib module — operators can build login, admin, and federation flows in scripts that the runtime binary ships with.

§Getting started

Compose AuthCtx into your axum state via axum::extract::FromRef (the engine binary’s EngineState<S> is the canonical recipe — see [assay_engine] for the wiring). Out-of-the-box you’ll need a store::UserStore + store::SessionStore; the store::PostgresUserStore / store::SqliteUserStore / store::PostgresSessionStore / store::SqliteSessionStore impls cover both backends.

use std::sync::Arc;
use assay_auth::AuthCtx;
use assay_auth::store::{SqliteUserStore, SqliteSessionStore};

let users = SqliteUserStore::new(pool.clone()).into_dyn();
let sessions = SqliteSessionStore::new(pool.clone()).into_dyn();
let ctx = AuthCtx::new(users, sessions);
// ctx is now ready to be plugged into your Router via FromRef.

For the full deployment shape (issuer, JWKS rotation, OIDC provider discovery, biscuit root key bootstrap, passkey RP setup, Zanzibar store) lean on assay_engine::run — it builds an AuthCtx from engine.toml, runs the auth migration, and serves everything on one port.

§Storage model

All auth tables live in the auth schema (PG) or attached auth database (SQLite, default ./data/auth.db). The migration runner (schema::migrate_postgres / schema::migrate_sqlite) records each applied version in engine.migrations under module = 'auth', keyed by MIGRATION_VERSION. Migrations are idempotent — every CREATE uses IF NOT EXISTS.

§Feature flags

The default feature auth pulls in every module. Slim builds can opt a la carte — see the per-module #[cfg(feature = "...")] gates below. backend-postgres and backend-sqlite are independent and both default-on; downstream binaries pick the one(s) they need.

§Phase trail

Module boundaries and per-module rationale live in plan 11. v0.2.0 alignment (Ory-replacement scope, biscuit-built-in posture, schema layout) lives in plan 12c §“v0.2.0 alignment”.

Re-exports§

pub use ctx::AuthCtx;
pub use error::Error;
pub use error::Result;
pub use gate::Caller;
pub use gate::CallerSource;
pub use router::engine_auth_router;
pub use router::oidc_spec_router;
pub use router::router;
pub use schema::MIGRATION_VERSION;
pub use schema::MODULE_NAME;

Modules§

admin
Cross-cutting admin HTTP API.
biscuit
Biscuit capability tokens — public-key signed bearers with Datalog policy, offline verification, and caller-side attenuation.
ctx
Composed auth context — the value engine state holds for the auth module.
error
Error types for the auth crate.
external_jwt
External JWT issuer pass-through validation (v0.3.2). Trust JWTs minted by an upstream OIDC provider (e.g. Hydra) without managing engine-side users. Configured via [[auth.external_issuers]] in engine.toml. See module-level docs for the why and how. External JWT issuer pass-through validation.
gate
Unified authentication + authorization gate.
jwt
JWT issuance + verification with key rotation backed by auth.jwks_keys.
oidc
OIDC client — discovery, PKCE, callback, userinfo.
oidc_provider
OIDC provider — full Hydra-equivalent identity provider.
passkey
WebAuthn / passkey registration + authentication.
password
Argon2id password hashing wrapper.
router
Auth HTTP routers.
schema
Auth module schema bootstrap + migration runner.
session
Session management — opaque server-side sessions backed by crate::store::SessionStore.
state
Shared state types for auth router composition.
store
Storage traits + concrete backends for the auth crate.
zanzibar
Zanzibar / ReBAC layer — Keto / SpiceDB-equivalent authorization.

Functions§

module_name
Stable module name registered in engine.modules and used as the schema/attach name on both backends. Engine boot inserts a row with name = MODULE_NAME when --enable=auth (or equivalent runtime signal) flips this module on.