fix(users): Add max wrong attempts for two-fa (#6247)

This commit is contained in:
Riddhiagrawal001
2024-10-17 12:56:52 +05:30
committed by GitHub
parent 881f5fd014
commit 2798f57560
8 changed files with 224 additions and 5 deletions

View File

@ -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,