One engine.
Eight ways in.
Authkestra orchestrates passkeys, OAuth2, TOTP, device flow and more behind a single Rust engine — chain the strategies you need, and a flow that doesn't hold together simply won't compile.
One engine, dispatched at compile time
Every request lands on the same Engine. It hands off to whichever strategy you configured — a passkey, an authenticator code, an OAuth2 provider, a device flow — verifies the credential, mints a session or token, and returns the result. Below, the diagram cycles through all four so you can see one engine carry every route, and the panel beside it shows the code that registers whichever strategy is active. Three of them are the same builder; the device flow is its own object, because there is no browser session to hang it off.
- Passkey
- Authenticator code
- OAuth2
- Device flow
Request → Engine → strategy → verified → minted → returned
let engine = Authkestra::builder() .provider(OAuth2Flow::new(github_provider)) .session_store(session_store) .build();Why Authkestra
Compile-time safety
The typestate builder pattern turns a misconfigured engine into a compiler error, not a 2am runtime surprise.
Stateless OAuth
State and nonce live in encrypted cookies, never your database — so the engine scales horizontally without a shared session store.
Flexible chaining
Token, session, basic, and custom strategies chain together in the engine, so a complex flow is composed, not bolted on.
Framework & database agnostic
The engine is pure Rust logic. Axum and Actix are isolated adapters, and storage is a trait you implement — no schema is ever assumed.
Eleven ways to prove who someone is
Each one is a strategy you hand to the same engine. Mix them, chain them, or ship just one — the shape of your code doesn't change.
Passkeys (WebAuthn)
Phishing-resistant sign-in backed by the platform authenticator — no password to steal.
TOTP (authenticator apps)
Time-based one-time codes for a second factor, enrolled and verified against your own store.
Magic link
A one-time link, minted server-side and delivered however you choose — consumed exactly once.
Email / SMS one-time codes
A short code with a real attempt budget, not just a timer — with an opt-in resend cooldown.
Recovery codes
A look-up secret authenticator (NIST SP 800-63B) — a fallback for whatever factor the account actually has, not tied to TOTP.
Stateless OAuth2
Social sign-in with the OAuth dance handled for you, state and nonce kept out of your database.
OIDC provider (client)
Verify identity against any OpenID Connect provider using discovery and cached signing keys.
Client credentials
Machine-to-machine authentication for services that authenticate as themselves, not a user.
Device flow
Sign in on a second screen — built for TVs, CLIs, and anything without a convenient browser.
Bot protection (CAPTCHA)
Challenge suspect traffic before it reaches your authentication logic, not after.
Device signatures
Proof-of-possession backed by an issuer attestation, for clients that must prove which device they are.
Authkestra vs. the world
In the Rust ecosystem you generally choose between a standalone identity provider and a library you embed. Authkestra sits in between: a modular identity engine, embedded in your binary, that plays all three OAuth2/OIDC roles — provider, client, and resource server — in one crate. Here is how it compares to a well-scoped embedded library and to the standalone servers, both of which are good at what they set out to do.
| Feature | Authkestra | oauth2-passkey | Rauthy / Kanidm |
|---|---|---|---|
| Category | Embedded identity engine | Embedded auth library | Standalone IdP server |
| OAuth2 / OIDC role | Provider, client, and resource server — one crate | Relying party only | Full provider support |
| Resource server | Native — DPoP (RFC 9449) & mTLS (RFC 8705) binding | Not offered | API validation |
| Configuration safety | Strict — typestate builder | Not documented | N/A — external config |
| State management | Stateless, encrypted cookies | Stateful — Redis or in-memory | Stateful, dedicated DB |
| Data architecture | Agnostic, trait-based — any DB | SQLite / PostgreSQL built in | 100% yours |
| Passwordless methods | 5 — passkeys, TOTP, magic link, email/SMS OTP, recovery codes | Passkeys (WebAuthn) | Passkeys, plus MFA |
| Fresh proof to enrol a factor | Required — closes the session-hijack-then-enrol gap | Not documented | Not documented |
| Device signatures | Built in — proof-of-possession + attestation | Not offered | Not offered |
| Bot / abuse protection | CAPTCHA — Turnstile, hCaptcha, reCAPTCHA | Not offered | Rate-limit & IP-block (Rauthy) |
vs. oauth2-passkey
A clean, framework-agnostic core for signing users in with Google-style OAuth2 and passkeys, backed by its own Redis/SQL session store. It's a relying party, not a provider — there's no OP to issue tokens for your own other services, and no resource-server side to validate them. Authkestra plays all three roles from one crate, with the same passkey ceremony plus four more passwordless methods alongside it.
vs. Rauthy & Kanidm
Both are excellent, fast standalone identity servers — the right call when you want a dedicated IdP microservice, and both take security at least as seriously as Authkestra does. Authkestra gives you the same OP capability without the extra service to deploy, the network hop, or the database to back up — plus a resource-server SDK and an OIDC client for your own app's other logins, which a standalone IdP doesn't hand you.
Working GitHub login, four lines in
Add the facade crate with the features you need, then build the engine. The typestate builder only lets you call .build() once every prerequisite it needs is actually in place.
authkestra = { version = "0.13", features = ["axum", "memory", "github"] }let github_provider = GithubProvider::new(client_id, client_secret, redirect_uri);let session_store: Arc<dyn SessionStore> = Arc::new(MemoryStore::default()); let auth_engine = Authkestra::builder() .provider(OAuth2Flow::new(github_provider)) .session_store(session_store) .build();