Two factor authentication : TOTP #5
@ -8,44 +8,44 @@ use crate::actors::bruteforce_actor::BruteForceActor;
|
|||||||
use crate::actors::users_actor::{ChangePasswordResult, LoginResult, UsersActor};
|
use crate::actors::users_actor::{ChangePasswordResult, LoginResult, UsersActor};
|
||||||
use crate::constants::{APP_NAME, MAX_FAILED_LOGIN_ATTEMPTS, MIN_PASS_LEN};
|
use crate::constants::{APP_NAME, MAX_FAILED_LOGIN_ATTEMPTS, MIN_PASS_LEN};
|
||||||
use crate::controllers::base_controller::{FatalErrorPage, redirect_user, redirect_user_for_login};
|
use crate::controllers::base_controller::{FatalErrorPage, redirect_user, redirect_user_for_login};
|
||||||
use crate::data::login_redirect_query::LoginRedirectQuery;
|
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, User};
|
||||||
|
|
||||||
struct BaseLoginPage {
|
struct BaseLoginPage<'a> {
|
||||||
danger: Option<String>,
|
danger: Option<String>,
|
||||||
success: Option<String>,
|
success: Option<String>,
|
||||||
page_title: &'static str,
|
page_title: &'static str,
|
||||||
app_name: &'static str,
|
app_name: &'static str,
|
||||||
redirect_uri: String,
|
redirect_uri: &'a LoginRedirect,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login/login.html")]
|
#[template(path = "login/login.html")]
|
||||||
struct LoginTemplate {
|
struct LoginTemplate<'a> {
|
||||||
_p: BaseLoginPage,
|
_p: BaseLoginPage<'a>,
|
||||||
login: String,
|
login: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login/password_reset.html")]
|
#[template(path = "login/password_reset.html")]
|
||||||
struct PasswordResetTemplate {
|
struct PasswordResetTemplate<'a> {
|
||||||
_p: BaseLoginPage,
|
_p: BaseLoginPage<'a>,
|
||||||
min_pass_len: usize,
|
min_pass_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login/choose_second_factor.html")]
|
#[template(path = "login/choose_second_factor.html")]
|
||||||
struct ChooseSecondFactorTemplate<'a> {
|
struct ChooseSecondFactorTemplate<'a> {
|
||||||
_p: BaseLoginPage,
|
_p: BaseLoginPage<'a>,
|
||||||
factors: &'a [TwoFactor],
|
factors: &'a [TwoFactor],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "login/opt_input.html")]
|
#[template(path = "login/opt_input.html")]
|
||||||
struct LoginWithOTPTemplate<'a> {
|
struct LoginWithOTPTemplate<'a> {
|
||||||
_p: BaseLoginPage,
|
_p: BaseLoginPage<'a>,
|
||||||
factor: &'a TwoFactor,
|
factor: &'a TwoFactor,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ pub struct LoginRequestBody {
|
|||||||
pub struct LoginRequestQuery {
|
pub struct LoginRequestQuery {
|
||||||
logout: Option<bool>,
|
logout: Option<bool>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
redirect: LoginRedirectQuery,
|
redirect: LoginRedirect,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authenticate user
|
/// Authenticate user
|
||||||
@ -155,7 +155,7 @@ pub async fn login_route(
|
|||||||
danger,
|
danger,
|
||||||
success,
|
success,
|
||||||
app_name: APP_NAME,
|
app_name: APP_NAME,
|
||||||
redirect_uri: query.redirect.get_encoded(),
|
redirect_uri: &query.redirect,
|
||||||
},
|
},
|
||||||
login,
|
login,
|
||||||
}
|
}
|
||||||
@ -177,7 +177,7 @@ pub struct ChangePasswordRequestBody {
|
|||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct PasswordResetQuery {
|
pub struct PasswordResetQuery {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
redirect: LoginRedirectQuery,
|
redirect: LoginRedirect,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset user password route
|
/// Reset user password route
|
||||||
@ -220,7 +220,7 @@ pub async fn reset_password_route(id: Identity, query: web::Query<PasswordResetQ
|
|||||||
danger,
|
danger,
|
||||||
success: None,
|
success: None,
|
||||||
app_name: APP_NAME,
|
app_name: APP_NAME,
|
||||||
redirect_uri: query.redirect.get_encoded(),
|
redirect_uri: &query.redirect,
|
||||||
},
|
},
|
||||||
min_pass_len: MIN_PASS_LEN,
|
min_pass_len: MIN_PASS_LEN,
|
||||||
}
|
}
|
||||||
@ -232,7 +232,7 @@ pub async fn reset_password_route(id: Identity, query: web::Query<PasswordResetQ
|
|||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct ChooseSecondFactorQuery {
|
pub struct ChooseSecondFactorQuery {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
redirect: LoginRedirectQuery,
|
redirect: LoginRedirect,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -253,7 +253,7 @@ pub async fn choose_2fa_method(id: Identity, query: web::Query<ChooseSecondFacto
|
|||||||
danger: None,
|
danger: None,
|
||||||
success: None,
|
success: None,
|
||||||
app_name: APP_NAME,
|
app_name: APP_NAME,
|
||||||
redirect_uri: query.redirect.get_encoded(),
|
redirect_uri: &query.redirect,
|
||||||
},
|
},
|
||||||
factors: &user.two_factor,
|
factors: &user.two_factor,
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ pub async fn choose_2fa_method(id: Identity, query: web::Query<ChooseSecondFacto
|
|||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct LoginWithOTPQuery {
|
pub struct LoginWithOTPQuery {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
redirect: LoginRedirectQuery,
|
redirect: LoginRedirect,
|
||||||
id: FactorID,
|
id: FactorID,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ pub async fn login_with_otp(id: Identity, query: web::Query<LoginWithOTPQuery>,
|
|||||||
success: None,
|
success: None,
|
||||||
page_title: "Two-Factor Auth",
|
page_title: "Two-Factor Auth",
|
||||||
app_name: APP_NAME,
|
app_name: APP_NAME,
|
||||||
redirect_uri: query.redirect.get_encoded(),
|
redirect_uri: &query.redirect,
|
||||||
},
|
},
|
||||||
factor,
|
factor,
|
||||||
}.render().unwrap())
|
}.render().unwrap())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Eq, PartialEq, Clone)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Eq, PartialEq, Clone)]
|
||||||
pub struct LoginRedirectQuery(String);
|
pub struct LoginRedirect(String);
|
||||||
|
|
||||||
impl LoginRedirectQuery {
|
impl LoginRedirect {
|
||||||
pub fn get(&self) -> &str {
|
pub fn get(&self) -> &str {
|
||||||
match self.0.starts_with('/') && !self.0.starts_with("//") {
|
match self.0.starts_with('/') && !self.0.starts_with("//") {
|
||||||
true => self.0.as_str(),
|
true => self.0.as_str(),
|
||||||
@ -14,7 +14,7 @@ impl LoginRedirectQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LoginRedirectQuery {
|
impl Default for LoginRedirect {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self("/".to_string())
|
Self("/".to_string())
|
||||||
}
|
}
|
@ -12,4 +12,4 @@ pub mod code_challenge;
|
|||||||
pub mod open_id_user_info;
|
pub mod open_id_user_info;
|
||||||
pub mod access_token;
|
pub mod access_token;
|
||||||
pub mod totp_key;
|
pub mod totp_key;
|
||||||
pub mod login_redirect_query;
|
pub mod login_redirect;
|
@ -1,5 +1,6 @@
|
|||||||
use crate::data::client::ClientID;
|
use crate::data::client::ClientID;
|
||||||
use crate::data::entity_manager::EntityManager;
|
use crate::data::entity_manager::EntityManager;
|
||||||
|
use crate::data::login_redirect::LoginRedirect;
|
||||||
use crate::data::totp_key::TotpKey;
|
use crate::data::totp_key::TotpKey;
|
||||||
use crate::utils::err::Res;
|
use crate::utils::err::Res;
|
||||||
|
|
||||||
@ -27,10 +28,10 @@ impl TwoFactor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login_url(&self, redirect_uri: &str) -> String {
|
pub fn login_url(&self, redirect_uri: &LoginRedirect) -> String {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
TwoFactorType::TOTP(_) => format!("/2fa_otp?id={}&redirect_uri={}",
|
TwoFactorType::TOTP(_) => format!("/2fa_otp?id={}&redirect_uri={}",
|
||||||
self.id.0, redirect_uri)
|
self.id.0, redirect_uri.get_encoded())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% extends "base_login_page.html" %}
|
{% extends "base_login_page.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form action="/login?redirect={{ _p.redirect_uri }}" method="post">
|
<form action="/login?redirect={{ _p.redirect_uri.get_encoded() }}" method="post">
|
||||||
<div>
|
<div>
|
||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input name="login" type="text" required class="form-control" id="floatingName" placeholder="unsername"
|
<input name="login" type="text" required class="form-control" id="floatingName" placeholder="unsername"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div style="margin-top: 10px;">
|
<div style="margin-top: 10px;">
|
||||||
<a href="/2fa_auth?redirect=_p.redirect_uri">Sign in using another factor</a><br />
|
<a href="/2fa_auth?redirect={{ _p.redirect_uri.get_encoded() }}">Sign in using another factor</a><br />
|
||||||
<a href="/logout">Sign out</a>
|
<a href="/logout">Sign out</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% extends "base_login_page.html" %}
|
{% extends "base_login_page.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form action="/reset_password?redirect={{ _p.redirect_uri }}" method="post" id="reset_password_form">
|
<form action="/reset_password?redirect={{ _p.redirect_uri.get_encoded() }}" method="post" id="reset_password_form">
|
||||||
<div>
|
<div>
|
||||||
<p>You need to configure a new password:</p>
|
<p>You need to configure a new password:</p>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user