-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(users): add support to verify 2FA using recovery code #4737
Conversation
crates/router/src/routes/app.rs
Outdated
.service(web::resource("/recovery_codes/verify").route(web::post().to(recovery_code_verify))) | ||
.service( | ||
web::resource("/recovery_codes/generate") | ||
.route(web::get().to(generate_recovery_codes)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#recovery_code or recovery_codes
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
crates/api_models/src/user.rs
Outdated
@@ -258,6 +258,11 @@ pub struct VerifyTotpRequest { | |||
pub totp: Option<Secret<String>>, | |||
} | |||
|
|||
#[derive(Debug, serde::Deserialize, serde::Serialize)] | |||
pub struct VerifyAccessCodeRequest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to recovery code.
@@ -215,6 +215,7 @@ impl From<Flow> for ApiIdentifier { | |||
| Flow::UpdateUserAccountDetails | |||
| Flow::TotpBegin | |||
| Flow::TotpVerify | |||
| Flow::AccessCodeVerify |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename
Ok(recovery_codes | ||
.into_iter() | ||
.map(|recovery_code| is_correct_password(candidate.clone(), recovery_code)) | ||
.collect::<Result<Vec<_>, _>>()? | ||
.into_iter() | ||
.position(|x| x)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok(recovery_codes | |
.into_iter() | |
.map(|recovery_code| is_correct_password(candidate.clone(), recovery_code)) | |
.collect::<Result<Vec<_>, _>>()? | |
.into_iter() | |
.position(|x| x)) | |
for (index, recovery_code) in recovery_codes.enumerate() { | |
let is_match = is_correct_password(candidate, recovery_code)?; | |
if is_match { | |
return Ok(Some(index)); | |
} | |
} | |
return Ok(None) |
crates/router/src/core/user.rs
Outdated
let matching_index = | ||
password::get_index_for_correct_recovery_code(req.recovery_code, recovery_codes.clone())?; | ||
|
||
if matching_index.is_none() { | ||
return Err(UserErrors::InvalidRecoveryCode.into()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let matching_index = | |
password::get_index_for_correct_recovery_code(req.recovery_code, recovery_codes.clone())?; | |
if matching_index.is_none() { | |
return Err(UserErrors::InvalidRecoveryCode.into()); | |
} | |
let matching_index = | |
password::get_index_for_correct_recovery_code(req.recovery_code, recovery_codes.clone())? | |
.ok_or(UserErrors::InvalidRecoveryCode.into())?; |
crates/router/src/core/user.rs
Outdated
return Err(UserErrors::TotpNotSetup.into()); | ||
} | ||
|
||
let recovery_codes = user_from_db |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this mut directly.
@@ -351,7 +351,8 @@ email_role_arn = "" # The amazon resource name ( arn ) of the role which | |||
sts_role_session_name = "" # An identifier for the assumed role session, used to uniquely identify a session. | |||
|
|||
[user] | |||
password_validity_in_days = 90 # Number of days after which password should be updated | |||
password_validity_in_days = 90 # Number of days after which password should be updated | |||
two_factor_auth_expiry_in_secs = 300 # Number of seconds after which 2FA should be done again if doing update/change from inside |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo, if we've two different keys in redis for totp and recovery codes then the expiry for them should be different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can consider this when will be adding other 2FA methods, for now anyways value for all of them will be same
Type of Change
Description
The PR adds support to verify 2FA using recovery code, in case TOTP is lost
Additional Changes
Motivation and Context
Closes #4736
How did you test it?
First Sign in for user for which 2FA is already set.
Then to verify 2FA:
Use the below curl to test it
If the recovery code is correct then response will be
200 ok
.Else proper error would be thrown
Checklist
cargo +nightly fmt --all
cargo clippy