use actix_web::{ error::{self, ErrorForbidden, ErrorInternalServerError, ErrorNotFound}, post, web, Result, }; use serde::Deserialize; use ovunto_security::{crypto::key::PublicKey, Key, Salt, TOTPGenerator, TOTP}; use crate::app::state::AppState; #[derive(Deserialize)] struct GetSaltForm { username: String, totp: TOTP, } #[post("/salt")] async fn get_salt(form: web::Form, state: web::Data) -> Result { let GetSaltForm { username, totp } = form.into_inner(); if let Ok(users) = state.users.lock() { if let Some(user) = users.get(&username) { if user.totp_generator.verify(totp) { Ok(user.salt.to_base64()) } else { Err(ErrorForbidden("Wrong TOTP")) } } else { Err(ErrorNotFound("User does not exist")) } } else { Err(ErrorInternalServerError("Poison error")) } } #[derive(Deserialize)] struct SignupForm { username: String, public_key: PublicKey, salt: Salt, totp_secret: Key, } #[post("/signup")] async fn signup(form: web::Form, state: web::Data) -> Result { let SignupForm { username, public_key, salt, totp_secret, } = form.into_inner(); let totp_generator = TOTPGenerator::from_secret(totp_secret); match state.add_user(username, public_key, salt, totp_generator) { Ok(user) => Ok(format!( "Welcome {}!\nYou can now authenticate requests using your authentication key.", user.username )), Err(_) => Err(error::ErrorConflict("Username already in use.")), } } pub fn config(cfg: &mut web::ServiceConfig) { cfg.service(signup).service(get_salt); }