use std::ops::Deref; use actix_web::{HttpResponse, Responder, web}; 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 factors auth", &user, None, None), user: user.deref(), }.render().unwrap()) } /// Configure a new TOTP authentication factor pub async fn add_totp_factor_route(user: CurrentUser, app_conf: web::Data) -> impl Responder { let key = TotpKey::new_random(); let qr_code = qrcode_generator::to_png_to_vec( key.url_for_user(&user, &app_conf), 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, &app_conf), 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()) }