use actix_remote_ip::RemoteIP; use actix_web::web::Data; use actix_web::{web, HttpResponse, Responder}; use light_openid::basic_state_manager::BasicStateManager; use crate::app_config::AppConfig; use crate::controllers::HttpResult; use crate::extractors::auth_extractor::AuthExtractor; #[derive(serde::Serialize)] struct StartOIDCResponse { url: String, } /// Start OIDC authentication pub async fn start_oidc(sm: Data, ip: RemoteIP) -> HttpResult { let prov = AppConfig::get().openid_provider(); let conf = light_openid::primitives::OpenIDConfig::load_from_url(prov.configuration_url).await?; Ok(HttpResponse::Ok().json(StartOIDCResponse { url: conf.gen_authorization_url( prov.client_id, &sm.gen_state(ip.0)?, &AppConfig::get().oidc_redirect_url(), ), })) } #[derive(serde::Deserialize)] pub struct FinishOpenIDLoginQuery { code: String, state: String, } /// Finish OIDC authentication pub async fn finish_oidc( sm: Data, remote_ip: RemoteIP, req: web::Json, auth: AuthExtractor, ) -> HttpResult { if let Err(e) = sm.validate_state(remote_ip.0, &req.state) { log::error!("Failed to validate OIDC CB state! {e}"); return Ok(HttpResponse::BadRequest().json("Invalid state!")); } let prov = AppConfig::get().openid_provider(); let conf = light_openid::primitives::OpenIDConfig::load_from_url(prov.configuration_url).await?; let (token, _) = conf .request_token( prov.client_id, prov.client_secret, &req.code, &AppConfig::get().oidc_redirect_url(), ) .await?; let (user_info, _) = conf.request_user_info(&token).await?; if user_info.email_verified != Some(true) { log::error!("Email is not verified!"); return Ok(HttpResponse::Unauthorized().json("Email unverified by IDP!")); } let mail = match user_info.email { Some(m) => m, None => { return Ok(HttpResponse::Unauthorized().json("Email not provided by the IDP!")); } }; auth.authenticate(mail); Ok(HttpResponse::Ok().finish()) } #[derive(serde::Serialize)] struct CurrentUser { id: String, } /// Get current authenticated user pub async fn current_user(auth: AuthExtractor) -> impl Responder { HttpResponse::Ok().json(CurrentUser { id: auth.id().unwrap_or_else(|| "Anonymous".to_string()), }) } /// Sign out pub async fn sign_out(auth: AuthExtractor) -> impl Responder { auth.sign_out(); HttpResponse::Ok().finish() }