Framework Integration
Authkestra provides first-class support for both the axum and actix-web web frameworks through lightweight adapter crates (authkestra-axum and authkestra-actix).
Prerequisites
Section titled “Prerequisites”Ensure you have enabled the correct web framework feature on the authkestra crate in your Cargo.toml.
[dependencies]authkestra = { version = "0.9", features = ["axum", "session"] }[dependencies]authkestra = { version = "0.9", features = ["actix", "session"] }Which extractor needs which engine
Section titled “Which extractor needs which engine”Both extractors below read what they need out of your router state, not out of a global. On
Axum that is a compile-time requirement: the AxumState derive (see Router Setup) generates the
FromRef impls, and it only generates the ones your engine’s typestate actually has. So the
engine alias you store in state decides which extractor compiles:
| Engine alias | .session_store() |
.jwt_secret() / .token_manager() |
AuthSession |
AuthToken |
|---|---|---|---|---|
AkWebAppEngine |
✅ | — | ✅ | ❌ compile error |
AkApiEngine |
— | ✅ | ❌ compile error | ✅ |
AkEngine |
✅ | ✅ | ✅ | ✅ |
Using AuthToken with an AkWebAppEngine state fails with
the trait bound Result<Arc<TokenManager>, AxumError>: FromRef<AppState> is not satisfied — the
fix is to build the engine with a token manager and store it as AkApiEngine or AkEngine, not to
change the handler.
On Actix the same requirement exists but is resolved at runtime from app_data: a handler taking
Option<AuthToken> compiles against any state and simply always yields None when no
TokenManager was registered.
The AuthSession Extractor
Section titled “The AuthSession Extractor”The primary way to interact with an authenticated user in a handler when using sessions is via the AuthSession extractor. It automatically validates the session from cookies and injects the user’s identity.
use authkestra::axum::{AuthSession, AxumError};use axum::response::{IntoResponse, Json};use serde_json::json;
pub async fn my_handler( session: Result<AuthSession, AxumError>) -> impl IntoResponse { match session { Ok(AuthSession(session_data)) => Json(json!({ "message": "Welcome!", "user_id": session_data.identity.external_id, })), Err(_) => Json(json!({ "error": "Not authenticated" })) }}Actix extractors reject with actix_web::Error, so the idiomatic form here is Option<AuthSession>
(or Result<AuthSession, actix_web::Error>) — there is no ActixError type.
use authkestra::actix::AuthSession;use actix_web::{get, HttpResponse, Responder};
#[get("/api/user")]async fn get_user(session: Option<AuthSession>) -> impl Responder { match session { Some(AuthSession(session_data)) => HttpResponse::Ok().json( serde_json::json!({ "message": "Welcome!", "user_id": session_data.identity.external_id, }) ), None => HttpResponse::Unauthorized().json( serde_json::json!({ "error": "Not authenticated" }) ), }}The AuthToken Extractor
Section titled “The AuthToken Extractor”For stateless JWT authentication, Authkestra provides the AuthToken extractor which reads and validates the Authorization: Bearer <token> header.
use authkestra::axum::{AuthToken, AxumError};use axum::response::{IntoResponse, Json};use serde_json::json;
pub async fn token_protected_handler( token: Result<AuthToken, AxumError>) -> impl IntoResponse { match token { Ok(AuthToken(claims)) => Json(json!({ "message": "Valid Token!", "sub": claims.sub, })), Err(_) => Json(json!({ "error": "Invalid or missing token" })) }}use authkestra::actix::AuthToken;use actix_web::{get, HttpResponse, Responder};
#[get("/api/protected")]async fn get_protected(token: Option<AuthToken>) -> impl Responder { match token { Some(AuthToken(claims)) => HttpResponse::Ok().json( serde_json::json!({ "message": "Valid Token!", "sub": claims.sub, }) ), None => HttpResponse::Unauthorized().json( serde_json::json!({ "error": "Invalid token" }) ), }}Router Setup
Section titled “Router Setup”To wire up standard authentication endpoints automatically, use axum_router() / axum_router_stateless() for Axum, or actix_scope() / actix_scope_stateless() for Actix-web on the built Engine. The Axum methods return an axum::Router; the Actix methods return an actix_web::Scope you mount with .service(...).
use authkestra::axum::{AxumExt, AxumState};use authkestra::core::AkWebAppEngine;use axum::Router;use tower_cookies::CookieManagerLayer;
#[derive(Clone, AxumState)]struct AppState { #[authkestra(engine)] auth: AkWebAppEngine,}
let state = AppState { auth: auth_engine.clone() };
let app: Router = Router::new() .merge(auth_engine.axum_router()) .layer(CookieManagerLayer::new()) .with_state(state);.with_state(state) is not optional decoration: axum_router() returns a Router<AppState>, and
the extractors resolve their dependencies out of that AppState. A Router<()> will not compile.
use actix_web::{App, HttpServer};use authkestra::actix::{ActixExt, ActixState};use authkestra::core::AkWebAppEngine;
#[derive(Clone, ActixState)]struct AppState { #[authkestra(engine)] auth: AkWebAppEngine,}
let state = AppState { auth: auth_engine };
HttpServer::new(move || { let app_state = state.clone(); let config_state = app_state.clone(); App::new() .configure(move |cfg| config_state.configure_authkestra(cfg)) .service(app_state.auth.actix_scope())})