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