use crate::infrastructure::{ auth_callback, auth_login, logout, PendingOAuthStore, UserState, UserStateInner, }; use dioxus::prelude::*; use axum::routing::get; use axum::Extension; use time::Duration; use tower_sessions::{cookie::Key, MemoryStore, SessionManagerLayer}; /// Start the Axum server with Dioxus fullstack, session management, /// and Keycloak OAuth routes. /// /// # Errors /// /// Returns `Error` if the tokio runtime or TCP listener fails to start. pub fn server_start(app: fn() -> Element) -> Result<(), super::Error> { tokio::runtime::Runtime::new()?.block_on(async move { let state: UserState = UserStateInner { access_token: "abcd".into(), sub: "abcd".into(), refresh_token: "abcd".into(), ..Default::default() } .into(); let key = Key::generate(); let store = MemoryStore::default(); let session = SessionManagerLayer::new(store) .with_secure(false) // Lax is required so the browser sends the session cookie // on the redirect back from Keycloak (cross-origin GET). // Strict would silently drop the cookie on that navigation. .with_same_site(tower_sessions::cookie::SameSite::Lax) .with_expiry(tower_sessions::Expiry::OnInactivity(Duration::hours(24))) .with_signed(key); let addr = dioxus_cli_config::fullstack_address_or_localhost(); let listener = tokio::net::TcpListener::bind(addr).await?; // Layers are applied AFTER serve_dioxus_application so they // wrap both the custom Axum routes AND the Dioxus server // function routes (e.g. check_auth needs Session access). let router = axum::Router::new() .route("/auth", get(auth_login)) .route("/auth/callback", get(auth_callback)) .route("/logout", get(logout)) .serve_dioxus_application(ServeConfig::new(), app) .layer(Extension(PendingOAuthStore::default())) .layer(Extension(state)) .layer(session); info!("Serving at {addr}"); axum::serve(listener, router.into_make_service()).await?; Ok(()) }) }