Skip to content

Commit

Permalink
Add support for MFA with Duo's Universal Prompt (dani-garcia#4637)
Browse files Browse the repository at this point in the history
* Add initial working Duo Universal Prompt support.

* Add db schema and models for Duo 2FA state storage

* store duo states in the database and validate during authentication

* cleanup & comments

* bump state/nonce length

* replace stray use of TimeDelta

* more cleanup

* bind Duo oauth flow to device id, drop redundant device type handling

* drop redundant alphanum string generation code

* error handling cleanup

* directly use JWT_VALIDITY_SECS constant instead of copying it to DuoClient instances

* remove redundant explicit returns, rustfmt

* rearrange constants, update comments, error message

* override charset on duo state column to ascii for mysql

* Reduce twofactor_duo_ctx state/nonce column size in postgres and maria

* Add fixes suggested by clippy

* rustfmt

* Update to use the make_http_request

* Don't handle OrganizationDuo

* move Duo API endpoint fmt strings out of macros and into format! calls

* Add missing indentation

Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>

* remove redundant expiry check when purging Duo contexts

---------

Co-authored-by: BlackDex <black.dex@gmail.com>
Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 24, 2024
1 parent de66e56 commit b4b2701
Show file tree
Hide file tree
Showing 18 changed files with 720 additions and 16 deletions.
16 changes: 13 additions & 3 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@
## Cron schedule of the job that cleans old auth requests from the auth request.
## Defaults to every minute. Set blank to disable this job.
# AUTH_REQUEST_PURGE_SCHEDULE="30 * * * * *"
##
## Cron schedule of the job that cleans expired Duo contexts from the database. Does nothing if Duo MFA is disabled or set to use the legacy iframe prompt.
## Defaults to every minute. Set blank to disable this job.
# DUO_CONTEXT_PURGE_SCHEDULE="30 * * * * *"

########################
### General settings ###
Expand Down Expand Up @@ -423,15 +427,21 @@
# YUBICO_SERVER=http://yourdomain.com/wsapi/2.0/verify

## Duo Settings
## You need to configure all options to enable global Duo support, otherwise users would need to configure it themselves
## You need to configure the DUO_IKEY, DUO_SKEY, and DUO_HOST options to enable global Duo support.
## Otherwise users will need to configure it themselves.
## Create an account and protect an application as mentioned in this link (only the first step, not the rest):
## https://help.bitwarden.com/article/setup-two-step-login-duo/#create-a-duo-security-account
## Then set the following options, based on the values obtained from the last step:
# DUO_IKEY=<Integration Key>
# DUO_SKEY=<Secret Key>
# DUO_IKEY=<Client ID>
# DUO_SKEY=<Client Secret>
# DUO_HOST=<API Hostname>
## After that, you should be able to follow the rest of the guide linked above,
## ignoring the fields that ask for the values that you already configured beforehand.
##
## If you want to attempt to use Duo's 'Traditional Prompt' (deprecated, iframe based) set DUO_USE_IFRAME to 'true'.
## Duo no longer supports this, but it still works for some integrations.
## If you aren't sure, leave this alone.
# DUO_USE_IFRAME=false

## Email 2FA settings
## Email token size
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE twofactor_duo_ctx;
8 changes: 8 additions & 0 deletions migrations/mysql/2024-06-05-131359_add_2fa_duo_store/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE twofactor_duo_ctx (
state VARCHAR(64) NOT NULL,
user_email VARCHAR(255) NOT NULL,
nonce VARCHAR(64) NOT NULL,
exp BIGINT NOT NULL,

PRIMARY KEY (state)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE twofactor_duo_ctx;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE twofactor_duo_ctx (
state VARCHAR(64) NOT NULL,
user_email VARCHAR(255) NOT NULL,
nonce VARCHAR(64) NOT NULL,
exp BIGINT NOT NULL,

PRIMARY KEY (state)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE twofactor_duo_ctx;
8 changes: 8 additions & 0 deletions migrations/sqlite/2024-06-05-131359_add_2fa_duo_store/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE twofactor_duo_ctx (
state TEXT NOT NULL,
user_email TEXT NOT NULL,
nonce TEXT NOT NULL,
exp INTEGER NOT NULL,

PRIMARY KEY (state)
);
2 changes: 1 addition & 1 deletion src/api/core/two_factor/duo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ async fn get_user_duo_data(uuid: &str, conn: &mut DbConn) -> DuoStatus {
}

// let (ik, sk, ak, host) = get_duo_keys();
async fn get_duo_keys_email(email: &str, conn: &mut DbConn) -> ApiResult<(String, String, String, String)> {
pub(crate) async fn get_duo_keys_email(email: &str, conn: &mut DbConn) -> ApiResult<(String, String, String, String)> {
let data = match User::find_by_mail(email, conn).await {
Some(u) => get_user_duo_data(&u.uuid, conn).await.data(),
_ => DuoData::global(),
Expand Down
Loading

0 comments on commit b4b2701

Please sign in to comment.