use core::time::Duration; use std::sync::Arc; use actix::Actor; use actix_identity::config::LogoutBehaviour; use actix_identity::IdentityMiddleware; use actix_remote_ip::RemoteIPConfig; use actix_session::storage::CookieSessionStore; use actix_session::SessionMiddleware; use actix_web::cookie::{Key, SameSite}; use actix_web::middleware::Logger; use actix_web::{get, middleware, web, App, HttpResponse, HttpServer}; use basic_oidc::actors::bruteforce_actor::BruteForceActor; use basic_oidc::actors::openid_sessions_actor::OpenIDSessionsActor; use basic_oidc::actors::providers_states_actor::ProvidersStatesActor; use basic_oidc::actors::users_actor::{UsersActor, UsersSyncBackend}; use basic_oidc::constants::*; use basic_oidc::controllers::assets_controller::assets_route; use basic_oidc::controllers::*; use basic_oidc::data::app_config::AppConfig; use basic_oidc::data::client::ClientManager; use basic_oidc::data::entity_manager::EntityManager; use basic_oidc::data::jwt_signer::JWTSigner; use basic_oidc::data::provider::ProvidersManager; use basic_oidc::data::user::User; use basic_oidc::data::webauthn_manager::WebAuthManager; use basic_oidc::middlewares::auth_middleware::AuthMiddleware; #[get("/health")] async fn health() -> &'static str { "Running" } #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); let config = AppConfig::get(); if !config.storage_path().exists() { log::error!( "Specified storage path {:?} does not exists!", config.storage_path() ); panic!() } let mut users = EntityManager::::open_or_create(config.users_file()) .expect("Failed to load users list!"); // Create initial user if required if users.is_empty() { log::info!("Create default {} user", DEFAULT_ADMIN_USERNAME); let default_admin = User { username: DEFAULT_ADMIN_USERNAME.to_string(), authorized_clients: None, admin: true, ..Default::default() }; users .insert(default_admin.clone()) .expect("Failed to create initial user!"); // Set default admin password users .change_user_password(&default_admin.uid, DEFAULT_ADMIN_PASSWORD, true) .expect("Failed to change default admin password!"); } let users_actor = UsersActor::new(users).start(); let bruteforce_actor = BruteForceActor::default().start(); let providers_states_actor = ProvidersStatesActor::default().start(); let openid_sessions_actor = OpenIDSessionsActor::default().start(); let jwt_signer = JWTSigner::gen_from_memory().expect("Failed to generate JWKS key"); let webauthn_manager = Arc::new(WebAuthManager::init(config)); let mut clients = ClientManager::open_or_create(config.clients_file()).expect("Failed to load clients list!"); clients.apply_environment_variables(); let clients = Arc::new(clients); let mut providers = ProvidersManager::open_or_create(config.providers_file()) .expect("Failed to load providers list!"); providers.apply_environment_variables(); let providers = Arc::new(providers); log::info!("Server will listen on {}", config.listen_address); let listen_address = config.listen_address.to_string(); HttpServer::new(move || { let session_mw = SessionMiddleware::builder( CookieSessionStore::default(), Key::from(config.token_key.as_bytes()), ) .cookie_name(SESSION_COOKIE_NAME.to_string()) .cookie_secure(config.secure_cookie()) .cookie_same_site(SameSite::Lax) .build(); let identity_middleware = IdentityMiddleware::builder() .logout_behaviour(LogoutBehaviour::PurgeSession) .visit_deadline(Some(Duration::from_secs(MAX_INACTIVITY_DURATION))) .login_deadline(Some(Duration::from_secs(MAX_SESSION_DURATION))) .build(); App::new() .app_data(web::Data::new(users_actor.clone())) .app_data(web::Data::new(bruteforce_actor.clone())) .app_data(web::Data::new(providers_states_actor.clone())) .app_data(web::Data::new(openid_sessions_actor.clone())) .app_data(web::Data::new(clients.clone())) .app_data(web::Data::new(providers.clone())) .app_data(web::Data::new(jwt_signer.clone())) .app_data(web::Data::new(webauthn_manager.clone())) .app_data(web::Data::new(RemoteIPConfig { proxy: AppConfig::get().proxy_ip.clone(), })) .wrap( middleware::DefaultHeaders::new().add(("Permissions-Policy", "interest-cohort=()")), ) .wrap(Logger::default()) .wrap(AuthMiddleware {}) .wrap(identity_middleware) .wrap(session_mw) // Main route .route( "/", web::get().to(|| async { HttpResponse::Found() .append_header(("Location", "/settings")) .finish() }), ) .route("/robots.txt", web::get().to(assets_controller::robots_txt)) // Health route .service(health) // Assets serving .route("/assets/{path:.*}", web::get().to(assets_route)) // Login pages .route("/logout", web::get().to(login_controller::logout_route)) .route("/login", web::get().to(login_controller::login_route)) .route("/login", web::post().to(login_controller::login_route)) .route( "/reset_password", web::get().to(login_controller::reset_password_route), ) .route( "/reset_password", web::post().to(login_controller::reset_password_route), ) .route( "/2fa_auth", web::get().to(login_controller::choose_2fa_method), ) .route("/2fa_otp", web::get().to(login_controller::login_with_otp)) .route("/2fa_otp", web::post().to(login_controller::login_with_otp)) .route( "/2fa_webauthn", web::get().to(login_controller::login_with_webauthn), ) // Login api .route( "/login/api/auth_webauthn", web::post().to(login_api::auth_webauthn), ) // Providers controller .route( "/login_with_prov", web::get().to(providers_controller::start_login), ) .route( OIDC_PROVIDER_CB_URI, web::get().to(providers_controller::finish_login), ) // Settings routes .route( "/settings", web::get().to(settings_controller::account_settings_details_route), ) .route( "/settings/change_password", web::get().to(settings_controller::change_password_route), ) .route( "/settings/change_password", web::post().to(settings_controller::change_password_route), ) .route( "/settings/two_factors", web::get().to(two_factors_controller::two_factors_route), ) .route( "/settings/two_factors/add_totp", web::get().to(two_factors_controller::add_totp_factor_route), ) .route( "/settings/two_factors/add_webauthn", web::get().to(two_factors_controller::add_webauthn_factor_route), ) // User API .route( "/settings/api/two_factor/save_totp_factor", web::post().to(two_factor_api::save_totp_factor), ) .route( "/settings/api/two_factor/save_webauthn_factor", web::post().to(two_factor_api::save_webauthn_factor), ) .route( "/settings/api/two_factor/delete_factor", web::post().to(two_factor_api::delete_factor), ) .route( "/settings/api/two_factor/clear_login_history", // Use POST to prevent CSRF web::post().to(two_factor_api::clear_login_history), ) // Admin routes .route( "/admin", web::get().to(|| async { HttpResponse::Found() .append_header(("Location", "/settings")) .finish() }), ) .route( "/admin/clients", web::get().to(admin_controller::clients_route), ) .route( "/admin/providers", web::get().to(admin_controller::providers_route), ) .route("/admin/users", web::get().to(admin_controller::users_route)) .route( "/admin/users", web::post().to(admin_controller::users_route), ) .route( "/admin/create_user", web::get().to(admin_controller::create_user), ) .route( "/admin/edit_user", web::get().to(admin_controller::edit_user), ) // Admin API .route( "/admin/api/find_username", web::post().to(admin_api::find_username), ) .route( "/admin/api/delete_user", web::post().to(admin_api::delete_user), ) // OpenID routes .route( "/.well-known/openid-configuration", web::get().to(openid_controller::get_configuration), ) .route(AUTHORIZE_URI, web::get().to(openid_controller::authorize)) .route(TOKEN_URI, web::post().to(openid_controller::token)) .route(CERT_URI, web::get().to(openid_controller::cert_uri)) .route( USERINFO_URI, web::post().to(openid_controller::user_info_post), ) .route( USERINFO_URI, web::get().to(openid_controller::user_info_get), ) }) .bind(listen_address)? .run() .await }