1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! 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:
//!
//! | Module | Replaces | Purpose |
//! | ---------------------- | ------------------------- | ----------------------------------------------------------- |
//! | [`session`] | Ory Kratos (sessions) | Cookie + CSRF session manager (Argon2id-backed) |
//! | [`password`] | Ory Kratos (passwords) | Argon2id PHC strings, peppered hashing |
//! | [`jwt`] | Hydra (JWT) | RS256 issue/verify with rotated JWKS |
//! | [`oidc`] | Kratos (federation) | OIDC **client** — log in via Google/Apple/GitHub/upstream |
//! | [`oidc_provider`] | Ory Hydra | Full OIDC **provider** — `/authorize`, `/token`, `/userinfo`, `/.well-known/*`, RFC 7009 revoke, RFC 7662 introspect, back-channel logout |
//! | [`passkey`] | Kratos (WebAuthn) | `webauthn-rs`-backed passkey register + auth ceremonies |
//! | [`zanzibar`] | Ory Keto / SpiceDB | ReBAC tuples + recursive-CTE walk on PG18 + SQLite |
//! | [`biscuit`] | (Ory has nothing) | Datalog-attenuable capability tokens — **always-on** |
//! | [`store`] | — | `UserStore` / `SessionStore` traits + PG / SQLite backends |
//! | [`admin`] | Ory 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.users` ⇄ `workflow.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.
//!
//! ```no_run
//! # async fn build() -> anyhow::Result<()> {
//! # let pool = sqlx::SqlitePool::connect("sqlite::memory:").await?;
//! 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.
//! # Ok(()) }
//! ```
//!
//! 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".
/// 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.
pub use AuthCtx;
pub use ;
pub use ;
pub use ;
pub use ;
/// 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.
pub const