use std::ops::Deref; use actix_web::{HttpResponse, Responder}; use askama::Template; use qrcode_generator::QrCodeEcc; use crate::controllers::settings_controller::BaseSettingsPage; use crate::data::app_config::AppConfig; use crate::data::current_user::CurrentUser; use crate::data::totp_key::TotpKey; use crate::data::user::User; use crate::data::webauthn_manager::WebAuthManagerReq; #[derive(Template)] #[template(path = "settings/two_factors_page.html")] struct TwoFactorsPage<'a> { _p: BaseSettingsPage, user: &'a User, } #[derive(Template)] #[template(path = "settings/add_2fa_totp_page.html")] struct AddTotpPage { _p: BaseSettingsPage, qr_code: String, account_name: String, secret_key: String, } #[derive(Template)] #[template(path = "settings/add_webauthn_page.html")] struct AddWebauhtnPage { _p: BaseSettingsPage, opaque_state: String, challenge_json: String, } /// Manage two factors authentication methods route pub async fn two_factors_route(user: CurrentUser) -> impl Responder { HttpResponse::Ok().body( TwoFactorsPage { _p: BaseSettingsPage::get("Two factor auth", &user, None, None), user: user.deref(), } .render() .unwrap(), ) } /// Configure a new TOTP authentication factor pub async fn add_totp_factor_route(user: CurrentUser) -> impl Responder { let key = TotpKey::new_random(); let qr_code = qrcode_generator::to_png_to_vec( key.url_for_user(&user, AppConfig::get()), QrCodeEcc::Low, 1024, ); let qr_code = match qr_code { Ok(q) => q, Err(e) => { log::error!("Failed to generate QrCode! {:?}", e); return HttpResponse::InternalServerError().body("Failed to generate QrCode!"); } }; HttpResponse::Ok().body( AddTotpPage { _p: BaseSettingsPage::get("New authenticator app", &user, None, None), qr_code: base64::encode(qr_code), account_name: key.account_name(&user, AppConfig::get()), secret_key: key.get_secret(), } .render() .unwrap(), ) } /// Configure a new security key factor pub async fn add_webauthn_factor_route( user: CurrentUser, manager: WebAuthManagerReq, ) -> impl Responder { let registration_request = match manager.start_register(&user) { Ok(r) => r, Err(e) => { log::error!("Failed to request new key! {:?}", e); return HttpResponse::InternalServerError() .body("Failed to generate request for registration!"); } }; let challenge_json = match serde_json::to_string(®istration_request.creation_challenge) { Ok(r) => r, Err(e) => { log::error!("Failed to serialize challenge! {:?}", e); return HttpResponse::InternalServerError().body("Failed to serialize challenge!"); } }; HttpResponse::Ok().body( AddWebauhtnPage { _p: BaseSettingsPage::get("New security key", &user, None, None), opaque_state: registration_request.opaque_state, challenge_json: urlencoding::encode(&challenge_json).to_string(), } .render() .unwrap(), ) }