mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 21:07:58 +08:00
fix(users): Add max wrong attempts for two-fa (#6247)
This commit is contained in:
@ -1686,6 +1686,13 @@ pub async fn verify_totp(
|
||||
return Err(UserErrors::TotpNotSetup.into());
|
||||
}
|
||||
|
||||
let user_totp_attempts =
|
||||
tfa_utils::get_totp_attempts_from_redis(&state, &user_token.user_id).await?;
|
||||
|
||||
if user_totp_attempts >= consts::user::TOTP_MAX_ATTEMPTS {
|
||||
return Err(UserErrors::MaxTotpAttemptsReached.into());
|
||||
}
|
||||
|
||||
let user_totp_secret = user_from_db
|
||||
.decrypt_and_get_totp_secret(&state)
|
||||
.await?
|
||||
@ -1702,6 +1709,13 @@ pub async fn verify_totp(
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
!= req.totp.expose()
|
||||
{
|
||||
let _ = tfa_utils::insert_totp_attempts_in_redis(
|
||||
&state,
|
||||
&user_token.user_id,
|
||||
user_totp_attempts + 1,
|
||||
)
|
||||
.await
|
||||
.inspect_err(|error| logger::error!(?error));
|
||||
return Err(UserErrors::InvalidTotp.into());
|
||||
}
|
||||
|
||||
@ -1856,15 +1870,31 @@ pub async fn verify_recovery_code(
|
||||
return Err(UserErrors::TwoFactorAuthNotSetup.into());
|
||||
}
|
||||
|
||||
let user_recovery_code_attempts =
|
||||
tfa_utils::get_recovery_code_attempts_from_redis(&state, &user_token.user_id).await?;
|
||||
|
||||
if user_recovery_code_attempts >= consts::user::RECOVERY_CODE_MAX_ATTEMPTS {
|
||||
return Err(UserErrors::MaxRecoveryCodeAttemptsReached.into());
|
||||
}
|
||||
|
||||
let mut recovery_codes = user_from_db
|
||||
.get_recovery_codes()
|
||||
.ok_or(UserErrors::InternalServerError)?;
|
||||
|
||||
let matching_index = utils::user::password::get_index_for_correct_recovery_code(
|
||||
let Some(matching_index) = utils::user::password::get_index_for_correct_recovery_code(
|
||||
&req.recovery_code,
|
||||
&recovery_codes,
|
||||
)?
|
||||
.ok_or(UserErrors::InvalidRecoveryCode)?;
|
||||
else {
|
||||
let _ = tfa_utils::insert_recovery_code_attempts_in_redis(
|
||||
&state,
|
||||
&user_token.user_id,
|
||||
user_recovery_code_attempts + 1,
|
||||
)
|
||||
.await
|
||||
.inspect_err(|error| logger::error!(?error));
|
||||
return Err(UserErrors::InvalidRecoveryCode.into());
|
||||
};
|
||||
|
||||
tfa_utils::insert_recovery_code_in_redis(&state, user_from_db.get_user_id()).await?;
|
||||
let _ = recovery_codes.remove(matching_index);
|
||||
@ -1924,10 +1954,17 @@ pub async fn terminate_two_factor_auth(
|
||||
}
|
||||
}
|
||||
|
||||
let current_flow = domain::CurrentFlow::new(user_token, domain::SPTFlow::TOTP.into())?;
|
||||
let current_flow = domain::CurrentFlow::new(user_token.clone(), domain::SPTFlow::TOTP.into())?;
|
||||
let next_flow = current_flow.next(user_from_db, &state).await?;
|
||||
let token = next_flow.get_token(&state).await?;
|
||||
|
||||
let _ = tfa_utils::delete_totp_attempts_from_redis(&state, &user_token.user_id)
|
||||
.await
|
||||
.inspect_err(|error| logger::error!(?error));
|
||||
let _ = tfa_utils::delete_recovery_code_attempts_from_redis(&state, &user_token.user_id)
|
||||
.await
|
||||
.inspect_err(|error| logger::error!(?error));
|
||||
|
||||
auth::cookies::set_cookie_response(
|
||||
user_api::TokenResponse {
|
||||
token: token.clone(),
|
||||
@ -1950,6 +1987,40 @@ pub async fn check_two_factor_auth_status(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn check_two_factor_auth_status_with_attempts(
|
||||
state: SessionState,
|
||||
user_token: auth::UserIdFromAuth,
|
||||
) -> UserResponse<user_api::TwoFactorStatus> {
|
||||
let user_from_db: domain::UserFromStorage = state
|
||||
.global_store
|
||||
.find_user_by_id(&user_token.user_id)
|
||||
.await
|
||||
.change_context(UserErrors::InternalServerError)?
|
||||
.into();
|
||||
if user_from_db.get_totp_status() == TotpStatus::NotSet {
|
||||
return Ok(ApplicationResponse::Json(user_api::TwoFactorStatus {
|
||||
status: None,
|
||||
}));
|
||||
};
|
||||
|
||||
let totp = user_api::TwoFactorAuthAttempts {
|
||||
is_completed: tfa_utils::check_totp_in_redis(&state, &user_token.user_id).await?,
|
||||
remaining_attempts: consts::user::TOTP_MAX_ATTEMPTS
|
||||
- tfa_utils::get_totp_attempts_from_redis(&state, &user_token.user_id).await?,
|
||||
};
|
||||
let recovery_code = user_api::TwoFactorAuthAttempts {
|
||||
is_completed: tfa_utils::check_recovery_code_in_redis(&state, &user_token.user_id).await?,
|
||||
remaining_attempts: consts::user::RECOVERY_CODE_MAX_ATTEMPTS
|
||||
- tfa_utils::get_recovery_code_attempts_from_redis(&state, &user_token.user_id).await?,
|
||||
};
|
||||
Ok(ApplicationResponse::Json(user_api::TwoFactorStatus {
|
||||
status: Some(user_api::TwoFactorAuthStatusResponseWithAttempts {
|
||||
totp,
|
||||
recovery_code,
|
||||
}),
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn create_user_authentication_method(
|
||||
state: SessionState,
|
||||
req: user_api::CreateUserAuthenticationMethodRequest,
|
||||
|
||||
Reference in New Issue
Block a user