Can force 2FA authent
This commit is contained in:
		@@ -16,6 +16,7 @@ pub struct AuthWebauthnRequest {
 | 
			
		||||
    credential: PublicKeyCredential,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub async fn auth_webauthn(
 | 
			
		||||
    id: Identity,
 | 
			
		||||
    req: web::Json<AuthWebauthnRequest>,
 | 
			
		||||
@@ -25,10 +26,6 @@ pub async fn auth_webauthn(
 | 
			
		||||
    users: web::Data<Addr<UsersActor>>,
 | 
			
		||||
    logger: ActionLogger,
 | 
			
		||||
) -> impl Responder {
 | 
			
		||||
    if !SessionIdentity(Some(&id)).need_2fa_auth() {
 | 
			
		||||
        return HttpResponse::Unauthorized().json("No 2FA required!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let user_id = SessionIdentity(Some(&id)).user_id();
 | 
			
		||||
 | 
			
		||||
    match manager.finish_authentication(&user_id, &req.opaque_state, &req.credential) {
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ use crate::controllers::base_controller::{
 | 
			
		||||
    build_fatal_error_page, redirect_user, redirect_user_for_login,
 | 
			
		||||
};
 | 
			
		||||
use crate::data::action_logger::{Action, ActionLogger};
 | 
			
		||||
use crate::data::force_2fa_auth::Force2FAAuth;
 | 
			
		||||
use crate::data::login_redirect::LoginRedirect;
 | 
			
		||||
use crate::data::provider::{Provider, ProvidersManager};
 | 
			
		||||
use crate::data::session_identity::{SessionIdentity, SessionStatus};
 | 
			
		||||
@@ -311,8 +312,9 @@ pub async fn choose_2fa_method(
 | 
			
		||||
    id: Option<Identity>,
 | 
			
		||||
    query: web::Query<ChooseSecondFactorQuery>,
 | 
			
		||||
    users: web::Data<Addr<UsersActor>>,
 | 
			
		||||
    force2faauth: Force2FAAuth,
 | 
			
		||||
) -> impl Responder {
 | 
			
		||||
    if !SessionIdentity(id.as_ref()).need_2fa_auth() {
 | 
			
		||||
    if !SessionIdentity(id.as_ref()).need_2fa_auth() && !force2faauth.force {
 | 
			
		||||
        log::trace!("User does not require 2fa auth, redirecting");
 | 
			
		||||
        return redirect_user_for_login(query.redirect.get());
 | 
			
		||||
    }
 | 
			
		||||
@@ -329,7 +331,7 @@ pub async fn choose_2fa_method(
 | 
			
		||||
    // Automatically choose factor if there is only one factor
 | 
			
		||||
    if user.get_distinct_factors_types().len() == 1 && !query.force_display {
 | 
			
		||||
        log::trace!("User has only one factor, using it by default");
 | 
			
		||||
        return redirect_user(&user.two_factor[0].login_url(&query.redirect));
 | 
			
		||||
        return redirect_user(&user.two_factor[0].login_url(&query.redirect, true));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    HttpResponse::Ok().content_type("text/html").body(
 | 
			
		||||
@@ -360,6 +362,7 @@ pub struct LoginWithOTPForm {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Login with OTP
 | 
			
		||||
#[allow(clippy::too_many_arguments)]
 | 
			
		||||
pub async fn login_with_otp(
 | 
			
		||||
    id: Option<Identity>,
 | 
			
		||||
    query: web::Query<LoginWithOTPQuery>,
 | 
			
		||||
@@ -368,10 +371,11 @@ pub async fn login_with_otp(
 | 
			
		||||
    http_req: HttpRequest,
 | 
			
		||||
    remote_ip: RemoteIP,
 | 
			
		||||
    logger: ActionLogger,
 | 
			
		||||
    force2faauth: Force2FAAuth,
 | 
			
		||||
) -> impl Responder {
 | 
			
		||||
    let mut danger = None;
 | 
			
		||||
 | 
			
		||||
    if !SessionIdentity(id.as_ref()).need_2fa_auth() {
 | 
			
		||||
    if !SessionIdentity(id.as_ref()).need_2fa_auth() && !force2faauth.force {
 | 
			
		||||
        return redirect_user_for_login(query.redirect.get());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -446,8 +450,9 @@ pub async fn login_with_webauthn(
 | 
			
		||||
    query: web::Query<LoginWithWebauthnQuery>,
 | 
			
		||||
    manager: WebAuthManagerReq,
 | 
			
		||||
    users: web::Data<Addr<UsersActor>>,
 | 
			
		||||
    force2faauth: Force2FAAuth,
 | 
			
		||||
) -> impl Responder {
 | 
			
		||||
    if !SessionIdentity(id.as_ref()).need_2fa_auth() {
 | 
			
		||||
    if !SessionIdentity(id.as_ref()).need_2fa_auth() && !force2faauth.force {
 | 
			
		||||
        return redirect_user_for_login(query.redirect.get());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										36
									
								
								src/data/force_2fa_auth.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/data/force_2fa_auth.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
use crate::data::current_user::CurrentUser;
 | 
			
		||||
use actix_web::dev::Payload;
 | 
			
		||||
use actix_web::{web, Error, FromRequest, HttpRequest};
 | 
			
		||||
use std::future::Future;
 | 
			
		||||
use std::pin::Pin;
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct Force2FAAuthQuery {
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    force_2fa: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Force2FAAuth {
 | 
			
		||||
    pub force: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FromRequest for Force2FAAuth {
 | 
			
		||||
    type Error = Error;
 | 
			
		||||
    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
 | 
			
		||||
 | 
			
		||||
    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
 | 
			
		||||
        let req = req.clone();
 | 
			
		||||
 | 
			
		||||
        let query = web::Query::<Force2FAAuthQuery>::from_request(&req, payload)
 | 
			
		||||
            .into_inner()
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            let user = CurrentUser::from_request(&req, &mut Payload::None).await?;
 | 
			
		||||
 | 
			
		||||
            Ok(Self {
 | 
			
		||||
                force: query.force_2fa && user.has_two_factor(),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,6 +5,7 @@ pub mod client;
 | 
			
		||||
pub mod code_challenge;
 | 
			
		||||
pub mod current_user;
 | 
			
		||||
pub mod entity_manager;
 | 
			
		||||
pub mod force_2fa_auth;
 | 
			
		||||
pub mod id_token;
 | 
			
		||||
pub mod jwt_signer;
 | 
			
		||||
pub mod login_redirect;
 | 
			
		||||
 
 | 
			
		||||
@@ -90,11 +90,17 @@ impl TwoFactor {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn login_url(&self, redirect_uri: &LoginRedirect) -> String {
 | 
			
		||||
    pub fn login_url(&self, redirect_uri: &LoginRedirect, force_2fa: bool) -> String {
 | 
			
		||||
        match self.kind {
 | 
			
		||||
            TwoFactorType::TOTP(_) => format!("/2fa_otp?redirect={}", redirect_uri.get_encoded()),
 | 
			
		||||
            TwoFactorType::TOTP(_) => format!(
 | 
			
		||||
                "/2fa_otp?redirect={}&force_2fa={force_2fa}",
 | 
			
		||||
                redirect_uri.get_encoded()
 | 
			
		||||
            ),
 | 
			
		||||
            TwoFactorType::WEBAUTHN(_) => {
 | 
			
		||||
                format!("/2fa_webauthn?redirect={}", redirect_uri.get_encoded())
 | 
			
		||||
                format!(
 | 
			
		||||
                    "/2fa_webauthn?redirect={}&force_2fa={force_2fa}",
 | 
			
		||||
                    redirect_uri.get_encoded()
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user