diff --git a/README.md b/README.md index e387800..ec49ffa 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Features : * [x] `authorization_code` flow * [x] Client authentication using secrets * [x] Bruteforce protection -* [ ] 2 factors authentication +* [x] 2 factor authentication * [x] TOTP (authenticator app) - * [x] Using a security key + * [x] Using a security key (Webauthn) * [ ] Fully responsive webui * [ ] `robots.txt` file to prevent indexing diff --git a/src/controllers/base_controller.rs b/src/controllers/base_controller.rs index 41939bb..3972fe7 100644 --- a/src/controllers/base_controller.rs +++ b/src/controllers/base_controller.rs @@ -29,6 +29,10 @@ pub fn redirect_user_for_login(redirect_uri: P) -> HttpResponse { /// Fatal error page message #[derive(Template)] #[template(path = "fatal_error.html")] -pub struct FatalErrorPage { - pub message: &'static str, +struct FatalErrorPage { + message: &'static str, +} + +pub fn build_fatal_error_page(msg: &'static str) -> String { + FatalErrorPage { message: msg }.render().unwrap() } \ No newline at end of file diff --git a/src/controllers/login_controller.rs b/src/controllers/login_controller.rs index 0ccdac8..613edc5 100644 --- a/src/controllers/login_controller.rs +++ b/src/controllers/login_controller.rs @@ -7,7 +7,7 @@ use crate::actors::{bruteforce_actor, users_actor}; use crate::actors::bruteforce_actor::BruteForceActor; use crate::actors::users_actor::{ChangePasswordResult, LoginResult, UsersActor}; 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::{build_fatal_error_page, redirect_user, redirect_user_for_login}; use crate::data::login_redirect::LoginRedirect; use crate::data::remote_ip::RemoteIP; use crate::data::session_identity::{SessionIdentity, SessionStatus}; @@ -91,9 +91,7 @@ pub async fn login_route( if failed_attempts > MAX_FAILED_LOGIN_ATTEMPTS { return HttpResponse::TooManyRequests().body( - FatalErrorPage { - message: "Too many failed login attempts, please try again later!" - }.render().unwrap() + build_fatal_error_page("Too many failed login attempts, please try again later!") ); } @@ -306,15 +304,13 @@ pub async fn login_with_otp(id: Identity, query: web::Query, let factor = match user.find_factor(&query.id) { Some(f) => f, - None => return HttpResponse::Ok() - .body(FatalErrorPage { message: "Factor not found!" }.render().unwrap()) + None => return HttpResponse::Ok().body(build_fatal_error_page("Factor not found!")) }; let key = match &factor.kind { TwoFactorType::TOTP(key) => key, _ => { - return HttpResponse::Ok() - .body(FatalErrorPage { message: "Factor is not a TOTP key!" }.render().unwrap()); + return HttpResponse::Ok().body(build_fatal_error_page("Factor is not a TOTP key!")); } }; @@ -360,15 +356,14 @@ pub async fn login_with_webauthn(id: Identity, query: web::Query f, - None => return HttpResponse::Ok() - .body(FatalErrorPage { message: "Factor not found!" }.render().unwrap()) + None => return HttpResponse::Ok().body(build_fatal_error_page("Factor not found!")) }; let key = match &factor.kind { TwoFactorType::WEBAUTHN(key) => key, _ => { return HttpResponse::Ok() - .body(FatalErrorPage { message: "Factor is not a Webauthn key!" }.render().unwrap()); + .body(build_fatal_error_page("Factor is not a Webauthn key!")); } }; @@ -377,7 +372,7 @@ pub async fn login_with_webauthn(id: Identity, query: web::Query { log::error!("Failed to generate webauthn challenge! {:?}", e); return HttpResponse::InternalServerError() - .body(FatalErrorPage { message: "Failed to generate webauthn challenge" }.render().unwrap()); + .body(build_fatal_error_page("Failed to generate webauthn challenge")); } }; diff --git a/src/controllers/openid_controller.rs b/src/controllers/openid_controller.rs index cd14a46..f25d9e8 100644 --- a/src/controllers/openid_controller.rs +++ b/src/controllers/openid_controller.rs @@ -4,13 +4,12 @@ use actix::Addr; use actix_identity::Identity; use actix_web::{HttpRequest, HttpResponse, Responder, web}; use actix_web::error::ErrorUnauthorized; -use askama::Template; use crate::actors::{openid_sessions_actor, users_actor}; use crate::actors::openid_sessions_actor::{OpenIDSessionsActor, Session, SessionID}; use crate::actors::users_actor::UsersActor; use crate::constants::*; -use crate::controllers::base_controller::FatalErrorPage; +use crate::controllers::base_controller::build_fatal_error_page; use crate::data::app_config::AppConfig; use crate::data::client::{ClientID, ClientManager}; use crate::data::code_challenge::CodeChallenge; @@ -100,18 +99,16 @@ pub async fn authorize(user: CurrentUser, id: Identity, query: web::Query>) -> impl Responder { let client = match clients.find_by_id(&query.client_id) { None => { - return HttpResponse::BadRequest().body(FatalErrorPage { - message: "Client is invalid!" - }.render().unwrap()); + return HttpResponse::BadRequest() + .body(build_fatal_error_page("Client is invalid!")); } Some(c) => c }; let redirect_uri = query.redirect_uri.trim().to_string(); if !redirect_uri.starts_with(&client.redirect_uri) { - return HttpResponse::BadRequest().body(FatalErrorPage { - message: "Redirect URI is invalid!" - }.render().unwrap()); + return HttpResponse::BadRequest() + .body(build_fatal_error_page("Redirect URI is invalid!")); } if !query.scope.split(' ').any(|x| x == "openid") { diff --git a/src/middlewares/auth_middleware.rs b/src/middlewares/auth_middleware.rs index 50944ae..97d1b8e 100644 --- a/src/middlewares/auth_middleware.rs +++ b/src/middlewares/auth_middleware.rs @@ -11,10 +11,9 @@ use actix_web::{ }; use actix_web::body::EitherBody; use actix_web::http::{header, Method}; -use askama::Template; use crate::constants::{ADMIN_ROUTES, AUTHENTICATED_ROUTES, AUTHORIZE_URI, TOKEN_URI, USERINFO_URI}; -use crate::controllers::base_controller::{FatalErrorPage, redirect_user_for_login}; +use crate::controllers::base_controller::{build_fatal_error_page, redirect_user_for_login}; use crate::data::app_config::AppConfig; use crate::data::session_identity::{SessionIdentity, SessionIdentityData, SessionStatus}; @@ -143,10 +142,8 @@ impl Service for AuthInnerMiddleware if !session.is_admin() && req.path().starts_with(ADMIN_ROUTES) { return Ok(req .into_response( - HttpResponse::Unauthorized() - .body(FatalErrorPage { - message: "You are not allowed to access this resource." - }.render().unwrap()), + HttpResponse::Unauthorized().body( + build_fatal_error_page("You are not allowed to access this resource.")), ) .map_into_right_body()); }