diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 0da381bd49..9db47b74dc 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -1634,7 +1634,39 @@ pub async fn begin_totp( let totp = tfa_utils::generate_default_totp(user_from_db.get_email(), None)?; let secret = totp.get_secret_base32().into(); + tfa_utils::insert_totp_secret_in_redis(&state, &user_token.user_id, &secret).await?; + Ok(ApplicationResponse::Json(user_api::BeginTotpResponse { + secret: Some(user_api::TotpSecret { + secret, + totp_url: totp.get_url().into(), + }), + })) +} + +pub async fn reset_totp( + state: AppState, + user_token: auth::UserFromToken, +) -> UserResponse { + let user_from_db: domain::UserFromStorage = state + .store + .find_user_by_id(&user_token.user_id) + .await + .change_context(UserErrors::InternalServerError)? + .into(); + + if user_from_db.get_totp_status() != TotpStatus::Set { + return Err(UserErrors::TotpNotSetup.into()); + } + + if !tfa_utils::check_totp_in_redis(&state, &user_token.user_id).await? + && !tfa_utils::check_recovery_code_in_redis(&state, &user_token.user_id).await? + { + return Err(UserErrors::TwoFactorAuthRequired.into()); + } + + let totp = tfa_utils::generate_default_totp(user_from_db.get_email(), None)?; + let secret = totp.get_secret_base32().into(); tfa_utils::insert_totp_secret_in_redis(&state, &user_token.user_id, &secret).await?; Ok(ApplicationResponse::Json(user_api::BeginTotpResponse { diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 141e9f53af..a7d17e602d 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1218,6 +1218,7 @@ impl User { .service( web::scope("/totp") .service(web::resource("/begin").route(web::get().to(totp_begin))) + .service(web::resource("/reset").route(web::get().to(totp_reset))) .service( web::resource("/verify") .route(web::post().to(totp_verify)) diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index 417a4360c5..b4b9658a7e 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -214,6 +214,7 @@ impl From for ApiIdentifier { | Flow::VerifyEmailRequest | Flow::UpdateUserAccountDetails | Flow::TotpBegin + | Flow::TotpReset | Flow::TotpVerify | Flow::TotpUpdate | Flow::RecoveryCodeVerify diff --git a/crates/router/src/routes/user.rs b/crates/router/src/routes/user.rs index f22e4aac52..e5cc7e4e29 100644 --- a/crates/router/src/routes/user.rs +++ b/crates/router/src/routes/user.rs @@ -648,6 +648,20 @@ pub async fn totp_begin(state: web::Data, req: HttpRequest) -> HttpRes .await } +pub async fn totp_reset(state: web::Data, req: HttpRequest) -> HttpResponse { + let flow = Flow::TotpReset; + Box::pin(api::server_wrap( + flow, + state.clone(), + &req, + (), + |state, user, _, _| user_core::reset_totp(state, user), + &auth::DashboardNoPermissionAuth, + api_locking::LockAction::NotApplicable, + )) + .await +} + pub async fn totp_verify( state: web::Data, req: HttpRequest, diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 728f2ed206..110532f524 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -404,6 +404,8 @@ pub enum Flow { UserFromEmail, /// Begin TOTP TotpBegin, + // Reset TOTP + TotpReset, /// Verify TOTP TotpVerify, /// Update TOTP secret