Stateless OAuth2
By default, OAuth2 logins can create a server-side session. However, Authkestra allows you to build a strictly stateless flow where authentication results in a signed JWT (JSON Web Token).
“Stateless” means that the entire flow can be completed without using a session store.
The OAuth2 Standard
Section titled “The OAuth2 Standard”Authkestra strictly implements the OAuth 2.0 Authorization Framework (RFC 6749) and PKCE (RFC 7636). We provide a memory-safe, idiomatic Rust engine to execute them.
Prerequisites
Section titled “Prerequisites”If you are using multiple OAuth providers (e.g., GitHub and Google), enable their respective feature flags:
[dependencies]authkestra = { version = "0.9", features = ["axum", "token", "github", "google"] }Stateless Engine Configuration
Section titled “Stateless Engine Configuration”Notice how we completely omit .session_store() and use .jwt_secret() instead. You can chain multiple providers, each wrapped in an OAuth2Flow:
use authkestra::Authkestra;use authkestra::providers::github::GithubProvider;use authkestra::providers::google::GoogleProvider;use authkestra_engine::OAuth2Flow;
// Initialize Authkestra in stateless mode supporting multiple providerslet auth_engine = Authkestra::builder() .provider(OAuth2Flow::new(GithubProvider::new( "GITHUB_CLIENT_ID".into(), "GITHUB_CLIENT_SECRET".into(), "http://localhost:3000/auth/callback/github".into(), ))) .provider(OAuth2Flow::new(GoogleProvider::new( "GOOGLE_CLIENT_ID".into(), "GOOGLE_CLIENT_SECRET".into(), "http://localhost:3000/auth/callback/google".into(), ))) .jwt_secret(b"your-256-bit-secret-key-at-least-32-bytes-long") .build();A runnable version of this setup lives in the repository:
cargo run -p authkestra --example axum_oauth_stateless --all-featuresWiring the Routes
Section titled “Wiring the Routes”Because we are not using sessions, we use the stateless router counterpart: auth_engine.axum_router_stateless() for Axum or auth_engine.actix_scope_stateless() for Actix-web. These live on
AxumStatelessExt / ActixStatelessExt, not on AxumExt / ActixExt — importing the wrong
trait is the usual cause of “no method named axum_router_stateless found”.
use authkestra::axum::{AxumState, AxumStatelessExt};use authkestra::core::AkApiEngine;
// The router's state must carry the token-configured engine; the derive generates the// `FromRef` impls `axum_router_stateless()` requires. A bare `Router<()>` will not compile.#[derive(Clone, AxumState)]struct AppState { #[authkestra(engine)] auth: AkApiEngine,}
let state = AppState { auth: auth_engine.clone() };
let app: axum::Router = axum::Router::new() .merge(auth_engine.axum_router_stateless()) .with_state(state);use authkestra::actix::{ActixState, ActixStatelessExt};use authkestra::core::AkApiEngine;
#[derive(Clone, ActixState)]struct AppState { #[authkestra(engine)] auth: AkApiEngine,}
let state = AppState { auth: auth_engine.clone() };
let app = actix_web::App::new() // `configure_authkestra` registers the token manager as app data so `AuthToken` resolves. .configure(move |cfg| state.configure_authkestra(cfg)) .service(auth_engine.actix_scope_stateless());Wiring Handlers Manually
Section titled “Wiring Handlers Manually”If you need absolute control over the HTTP response, you can bypass the automatic router and handle requests manually using Authkestra’s helpers in authkestra-axum or authkestra-actix.
use axum::{extract::{Path, State, Query}, response::IntoResponse};use authkestra::axum::helpers;
async fn login_handler( Path(provider): Path<String>, State(state): State<AppState>, Query(params): Query<helpers::OAuthLoginParams>, cookies: tower_cookies::Cookies,) -> impl IntoResponse { helpers::axum_login_handler::<AppState, _, _>(Path(provider), State(state), Query(params), cookies).await}use actix_web::{web, Responder};use authkestra::actix::helpers;
// `Engine<S, T>` is the typestate engine itself — `S`/`T` are the session-store and// token-manager states, not your application state.async fn login_handler( path: web::Path<String>, authkestra: web::Data<AkApiEngine>, params: web::Query<helpers::OAuthLoginParams>,) -> impl Responder { helpers::actix_login_handler(path, authkestra, params).await}By manually exposing these handlers, you gain full control over the HTTP response. You can append headers, record analytics, or return the JWT inside a custom JSON body.