Can sign in and sign out
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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<User> = 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<AuthCallbackQuery>) -> HttpResult { | ||||
|     if session.get(STATE_KEY)? != Some(query.state.to_string()) { | ||||
|         return Ok(HttpResponse::BadRequest() | ||||
|             .append_header(("content-type", "text/html")) | ||||
|             .body("State mismatch! <a href='/'>Try again</a>")); | ||||
|     } | ||||
|  | ||||
|     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! <a href='/'>Try again</a>")); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     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()) | ||||
| } | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user