From 2f8fd66648bd3b6bde28147c3d14c1b0e1e5bd22 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Tue, 21 Jan 2025 21:40:07 +0100 Subject: [PATCH] Can sign in and sign out --- src/main.rs | 2 ++ src/server/web_ui.rs | 74 +++++++++++++++++++++++++++++++++++++++++--- src/user.rs | 6 ++-- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9d8bdd1..990a0db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,8 @@ async fn main() -> std::io::Result<()> { )) // Web configuration routes .route("/", web::get().to(web_ui::home)) + .route("/oidc_cb", web::get().to(web_ui::oidc_cb)) + .route("/sign_out", web::get().to(web_ui::sign_out)) // API routes // TODO diff --git a/src/server/web_ui.rs b/src/server/web_ui.rs index bd725ac..ff24619 100644 --- a/src/server/web_ui.rs +++ b/src/server/web_ui.rs @@ -1,17 +1,18 @@ use crate::app_config::AppConfig; use crate::constants::{STATE_KEY, USER_SESSION_KEY}; use crate::server::{HttpFailure, HttpResult}; -use crate::user::User; +use crate::user::{User, UserID}; use crate::utils; use actix_session::Session; -use actix_web::HttpResponse; +use actix_web::{web, HttpResponse}; use light_openid::primitives::OpenIDConfig; +// Main route pub async fn home(session: Session) -> HttpResult { - // Get user information + // Get user information, requesting authentication if information is missing let Some(user): Option = session.get(USER_SESSION_KEY)? else { // Generate auth state - let state = utils::rand_str(10); + let state = utils::rand_str(50); session.insert(STATE_KEY, &state)?; let oidc = AppConfig::get().openid_provider(); @@ -28,3 +29,68 @@ pub async fn home(session: Session) -> HttpResult { Ok(HttpResponse::Ok().body("You are authenticated!")) } + +#[derive(serde::Deserialize)] +pub struct AuthCallbackQuery { + code: String, + state: String, +} + +// Authenticate user callback +pub async fn oidc_cb(session: Session, query: web::Query) -> HttpResult { + if session.get(STATE_KEY)? != Some(query.state.to_string()) { + return Ok(HttpResponse::BadRequest() + .append_header(("content-type", "text/html")) + .body("State mismatch! Try again")); + } + + let oidc = AppConfig::get().openid_provider(); + let config = OpenIDConfig::load_from_url(oidc.configuration_url) + .await + .map_err(HttpFailure::OpenID)?; + + let (token, _) = match config + .request_token( + oidc.client_id, + oidc.client_secret, + &query.code, + &oidc.redirect_url, + ) + .await + { + Ok(t) => t, + Err(e) => { + log::error!("Failed to request user token! {e}"); + + return Ok(HttpResponse::BadRequest() + .append_header(("content-type", "text/html")) + .body("Authentication failed! Try again")); + } + }; + + let (user, _) = config + .request_user_info(&token) + .await + .map_err(HttpFailure::OpenID)?; + + let user = User { + id: UserID(user.sub), + name: user.name.unwrap_or("no_name".to_string()), + email: user.email.unwrap_or("no@mail.com".to_string()), + }; + log::info!("Successful authentication as {:?}", user); + session.insert(USER_SESSION_KEY, user)?; + + Ok(HttpResponse::Found() + .insert_header(("location", "/")) + .finish()) +} + +/// De-authenticate user +pub async fn sign_out(session: Session) -> HttpResult { + session.remove(USER_SESSION_KEY); + + Ok(HttpResponse::Found() + .insert_header(("location", "/")) + .finish()) +} diff --git a/src/user.rs b/src/user.rs index 22c9299..52a3149 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,7 +1,7 @@ -#[derive(Clone, serde::Serialize, serde::Deserialize)] -pub struct UserID(String); +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct UserID(pub String); -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct User { pub id: UserID, pub name: String,