Compare commits

..

2 Commits

Author SHA1 Message Date
7060ce3fe4 Enforce 2FA for user admin routes
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-27 21:03:49 +01:00
cf0e7e1e68 Can enforce 2FA for specific clients 2024-03-27 20:59:29 +01:00
7 changed files with 28 additions and 7 deletions

View File

@@ -20,8 +20,10 @@ You can configure a list of clients (Relying Parties) in a `clients.yaml` file w
redirect_uri: https://mygit.mywebsite.com/
# If you want new accounts to be granted access to this client by default
default: true
# If you want the client to be granted to every users, regardless their account configuration
# If you want the client to be granted to every user, regardless their account configuration
granted_to_all_users: true
# If you want users to have performed recent second factor authentication before accessing this client, set this setting to true
enforce_2fa_auth: true
```
On the first run, BasicOIDC will create a new administrator with credentials `admin` / `admin`. On first login you will have to change these default credentials.
@@ -32,7 +34,7 @@ In order to run BasicOIDC for development, you will need to create a least an em
* [x] `authorization_code` flow
* [x] Client authentication using secrets
* [x] Bruteforce protection
* [x] 2 factor authentication
* [x] 2 factors authentication
* [x] TOTP (authenticator app)
* [x] Using a security key (Webauthn)
* [ ] Fully responsive webui

View File

@@ -4,6 +4,7 @@ use actix_web::{web, HttpResponse, Responder};
use crate::actors::users_actor::{DeleteUserRequest, FindUserByUsername, UsersActor};
use crate::data::action_logger::{Action, ActionLogger};
use crate::data::critical_route::CriticalRoute;
use crate::data::current_user::CurrentUser;
use crate::data::user::UserID;
use crate::utils::string_utils;
@@ -19,6 +20,7 @@ struct FindUserResult {
}
pub async fn find_username(
_critical: CriticalRoute,
req: web::Form<FindUserNameReq>,
users: web::Data<Addr<UsersActor>>,
) -> impl Responder {
@@ -41,6 +43,7 @@ pub struct DeleteUserReq {
}
pub async fn delete_user(
_critical: CriticalRoute,
user: CurrentUser,
req: web::Form<DeleteUserReq>,
users: web::Data<Addr<UsersActor>>,

View File

@@ -12,6 +12,7 @@ use crate::controllers::settings_controller::BaseSettingsPage;
use crate::data::action_logger::{Action, ActionLogger};
use crate::data::app_config::AppConfig;
use crate::data::client::{Client, ClientID, ClientManager};
use crate::data::critical_route::CriticalRoute;
use crate::data::current_user::CurrentUser;
use crate::data::provider::{Provider, ProviderID, ProvidersManager};
use crate::data::user::{GeneralSettings, GrantedClients, User, UserID};
@@ -98,6 +99,7 @@ pub struct UpdateUserQuery {
}
pub async fn users_route(
_critical: CriticalRoute,
admin: CurrentUser,
users: web::Data<Addr<UsersActor>>,
update_query: Option<web::Form<UpdateUserQuery>>,
@@ -299,6 +301,7 @@ pub async fn users_route(
}
pub async fn create_user(
_critical: CriticalRoute,
admin: CurrentUser,
clients: web::Data<Arc<ClientManager>>,
providers: web::Data<Arc<ProvidersManager>>,
@@ -332,6 +335,7 @@ pub struct EditUserQuery {
}
pub async fn edit_user(
_critical: CriticalRoute,
admin: CurrentUser,
clients: web::Data<Arc<ClientManager>>,
providers: web::Data<Arc<ProvidersManager>>,

View File

@@ -13,7 +13,7 @@ use crate::actors::openid_sessions_actor::{OpenIDSessionsActor, Session, Session
use crate::actors::users_actor::UsersActor;
use crate::actors::{openid_sessions_actor, users_actor};
use crate::constants::*;
use crate::controllers::base_controller::build_fatal_error_page;
use crate::controllers::base_controller::{build_fatal_error_page, redirect_user};
use crate::data::action_logger::{Action, ActionLogger};
use crate::data::app_config::AppConfig;
use crate::data::client::{ClientID, ClientManager};
@@ -21,6 +21,7 @@ use crate::data::code_challenge::CodeChallenge;
use crate::data::current_user::CurrentUser;
use crate::data::id_token::IdToken;
use crate::data::jwt_signer::{JWTSigner, JsonWebKey};
use crate::data::login_redirect::{get_2fa_url, LoginRedirect};
use crate::data::session_identity::SessionIdentity;
use crate::data::user::User;
@@ -128,6 +129,7 @@ fn error_redirect(query: &AuthorizeQuery, error: &str, description: &str) -> Htt
}
pub async fn authorize(
req: HttpRequest,
user: CurrentUser,
id: Identity,
query: web::Query<AuthorizeQuery>,
@@ -142,6 +144,12 @@ pub async fn authorize(
Some(c) => c,
};
// Check if 2FA is required
if client.enforce_2fa_auth && user.should_request_2fa_for_critical_functions() {
let uri = get_2fa_url(&LoginRedirect::from_req(&req), true);
return redirect_user(&uri);
}
let redirect_uri = query.redirect_uri.trim().to_string();
if !redirect_uri.starts_with(&client.redirect_uri) {
return HttpResponse::BadRequest().body(build_fatal_error_page("Redirect URI is invalid!"));

View File

@@ -28,6 +28,10 @@ pub struct Client {
/// Specify whether a client is granted to all users
#[serde(default = "bool::default")]
pub granted_to_all_users: bool,
/// Specify whether recent Second Factor Authentication is required to access this client
#[serde(default = "bool::default")]
pub enforce_2fa_auth: bool,
}
impl PartialEq for Client {

View File

@@ -20,10 +20,10 @@ impl FromRequest for CriticalRoute {
.await
.expect("Failed to extract user identity!");
if current_user.should_request_2fa_for_critical_function() {
let url = get_2fa_url(&LoginRedirect::from_req(&req), true);
if current_user.should_request_2fa_for_critical_functions() {
let uri = get_2fa_url(&LoginRedirect::from_req(&req), true);
return Err(FromRequestRedirect::new(url));
return Err(FromRequestRedirect::new(uri));
}
Ok(Self)

View File

@@ -22,7 +22,7 @@ pub struct CurrentUser {
}
impl CurrentUser {
pub fn should_request_2fa_for_critical_function(&self) -> bool {
pub fn should_request_2fa_for_critical_functions(&self) -> bool {
self.user.has_two_factor()
&& self
.last_2fa_auth