Two factor authentication : TOTP #5

Merged
pierre merged 22 commits from twofactors into master 2022-04-20 07:40:51 +00:00
2 changed files with 33 additions and 6 deletions
Showing only changes of commit 1e401a0b10 - Show all commits

View File

@ -11,7 +11,7 @@ use crate::controllers::base_controller::{FatalErrorPage, redirect_user, redirec
use crate::data::login_redirect::LoginRedirect; use crate::data::login_redirect::LoginRedirect;
use crate::data::remote_ip::RemoteIP; use crate::data::remote_ip::RemoteIP;
use crate::data::session_identity::{SessionIdentity, SessionStatus}; use crate::data::session_identity::{SessionIdentity, SessionStatus};
use crate::data::user::{FactorID, TwoFactor, User}; use crate::data::user::{FactorID, TwoFactor, TwoFactorType, User};
struct BaseLoginPage<'a> { struct BaseLoginPage<'a> {
danger: Option<String>, danger: Option<String>,
@ -235,7 +235,6 @@ pub struct ChooseSecondFactorQuery {
redirect: LoginRedirect, redirect: LoginRedirect,
} }
/// Let the user select the factor to use to authenticate /// Let the user select the factor to use to authenticate
pub async fn choose_2fa_method(id: Identity, query: web::Query<ChooseSecondFactorQuery>, pub async fn choose_2fa_method(id: Identity, query: web::Query<ChooseSecondFactorQuery>,
users: web::Data<Addr<UsersActor>>) -> impl Responder { users: web::Data<Addr<UsersActor>>) -> impl Responder {
@ -269,10 +268,18 @@ pub struct LoginWithOTPQuery {
id: FactorID, id: FactorID,
} }
#[derive(serde::Deserialize)]
pub struct LoginWithOTPForm {
code: String,
}
/// Login with OTP /// Login with OTP
pub async fn login_with_otp(id: Identity, query: web::Query<LoginWithOTPQuery>, pub async fn login_with_otp(id: Identity, query: web::Query<LoginWithOTPQuery>,
form: Option<web::Form<LoginWithOTPForm>>,
users: web::Data<Addr<UsersActor>>) -> impl Responder { users: web::Data<Addr<UsersActor>>) -> impl Responder {
let mut danger = None;
if !SessionIdentity(&id).need_2fa_auth() { if !SessionIdentity(&id).need_2fa_auth() {
return redirect_user_for_login(query.redirect.get()); return redirect_user_for_login(query.redirect.get());
} }
@ -286,9 +293,26 @@ pub async fn login_with_otp(id: Identity, query: web::Query<LoginWithOTPQuery>,
.body(FatalErrorPage { message: "Factor not found!" }.render().unwrap()) .body(FatalErrorPage { message: "Factor not found!" }.render().unwrap())
}; };
let key = match &factor.kind {
TwoFactorType::TOTP(key) => key,
_ => {
return HttpResponse::Ok()
.body(FatalErrorPage { message: "Factor is not a TOTP key!" }.render().unwrap());
}
};
if let Some(form) = form {
if !key.check_code(&form.code).unwrap_or(false) {
danger = Some("Specified code is invalid!".to_string());
} else {
SessionIdentity(&id).set_status(SessionStatus::SignedIn);
return redirect_user(query.redirect.get());
}
}
HttpResponse::Ok().body(LoginWithOTPTemplate { HttpResponse::Ok().body(LoginWithOTPTemplate {
_p: BaseLoginPage { _p: BaseLoginPage {
danger: None, danger,
success: None, success: None,
page_title: "Two-Factor Auth", page_title: "Two-Factor Auth",
app_name: APP_NAME, app_name: APP_NAME,

View File

@ -12,7 +12,8 @@ pub struct FactorID(pub String);
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum TwoFactorType { pub enum TwoFactorType {
TOTP(TotpKey) TOTP(TotpKey),
_OTHER,
} }
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@ -25,14 +26,16 @@ pub struct TwoFactor {
impl TwoFactor { impl TwoFactor {
pub fn type_str(&self) -> &'static str { pub fn type_str(&self) -> &'static str {
match self.kind { match self.kind {
TwoFactorType::TOTP(_) => "Authenticator app" TwoFactorType::TOTP(_) => "Authenticator app",
_ => unimplemented!()
} }
} }
pub fn login_url(&self, redirect_uri: &LoginRedirect) -> String { pub fn login_url(&self, redirect_uri: &LoginRedirect) -> String {
match self.kind { match self.kind {
TwoFactorType::TOTP(_) => format!("/2fa_otp?id={}&redirect={}", TwoFactorType::TOTP(_) => format!("/2fa_otp?id={}&redirect={}",
self.id.0, redirect_uri.get_encoded()) self.id.0, redirect_uri.get_encoded()),
_ => unimplemented!()
} }
} }
} }