Skip to content

Commit

Permalink
Merge pull request openwebwork#2335 from drgrice1/two-factor-authenti…
Browse files Browse the repository at this point in the history
…cation

Add two factor authentication.
  • Loading branch information
Alex-Jordan authored Mar 20, 2024
2 parents 07e6c7b + 62bda1b commit d497e06
Show file tree
Hide file tree
Showing 19 changed files with 667 additions and 39 deletions.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ RUN apt-get update \
libfile-find-rule-perl-perl \
libfile-sharedir-install-perl \
libfuture-asyncawait-perl \
libgd-barcode-perl \
libgd-perl \
libhtml-scrubber-perl \
libhtml-template-perl \
Expand All @@ -113,6 +114,7 @@ RUN apt-get update \
libmail-sender-perl \
libmariadb-dev \
libmath-random-secure-perl \
libmime-base32-perl \
libmime-tools-perl \
libminion-backend-sqlite-perl \
libminion-perl \
Expand Down
2 changes: 2 additions & 0 deletions DockerfileStage1
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ RUN apt-get update \
libfile-find-rule-perl-perl \
libfile-sharedir-install-perl \
libfuture-asyncawait-perl \
libgd-barcode-perl \
libgd-perl \
libhtml-scrubber-perl \
libhtml-template-perl \
Expand All @@ -75,6 +76,7 @@ RUN apt-get update \
libmail-sender-perl \
libmariadb-dev \
libmath-random-secure-perl \
libmime-base32-perl \
libmime-tools-perl \
libminion-backend-sqlite-perl \
libminion-perl \
Expand Down
2 changes: 2 additions & 0 deletions bin/check_modules.pl
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ =head1 DESCRIPTION
File::Temp
Future::AsyncAwait
GD
GD::Barcode::QRcode
Getopt::Long
Getopt::Std
HTML::Entities
Expand All @@ -117,6 +118,7 @@ =head1 DESCRIPTION
Locale::Maketext::Lexicon
Locale::Maketext::Simple
LWP::Protocol::https
MIME::Base32
MIME::Base64
Math::Random::Secure
Minion
Expand Down
19 changes: 19 additions & 0 deletions bin/reset2fa
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
warn "Pass users as additional arguments on the command line.\n"
. "Usage: wwsh $ce->{courseName} /opt/webwork/webwork2/bin/reset2fa [users]\n"
unless @ARGV;

for (@ARGV) {
my $password = eval { $db->getPassword($_) };
if ($@) {
warn "Unable to retrieve password record for $_ from the database: $@\n";
next;
}

$password->otp_secret('');
eval { $db->putPassword($password) };
if ($@) {
warn "Unable to reset two factor authentication secret for $_: $@\n";
} else {
print "Successfully reset two factor authentication for $_.\n";
}
}
61 changes: 61 additions & 0 deletions conf/defaults.config
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ $authen{admin_module} = ['WeBWorK::Authen::Basic_TheLastOption'];
%permissionLevels = (
login => "guest",
navigation_allowed => "guest",
use_two_factor_auth => "student",
report_bugs => "ta",
submit_feedback => "student",
change_password => "student",
Expand Down Expand Up @@ -944,6 +945,66 @@ $CookieSecure = 0;
# when the browser session ends. The default value is 7 days.
$CookieLifeTime = 604800;

################################################################################
# Two Factor Authentication
################################################################################

# The following variables enable two factor authentication and control how it
# works. Two factor authentication only applies to courses that use password
# authentication, i.e., the Basic_TheLastOption user authentication module
# without an external authentication approach (like LTI, CAS, Shibboleth, etc.).
# It is recommended that two factor authentication be enabled for all courses
# that use password authentication. It is extremely highly recommended that this
# be enabled for the admin course. Two factor authentication works with an
# authenticator app on a mobile device (such as Google Authenticator,
# Microsoft authenticator, Twilio Authy, etc.).

# $twoFA{enabled} determines if two factor authentication is enabled for a
# course. If this is set to 0, then two factor authentication is disabled for
# all courses. If this is 1 (the default), then two factor authentication is
# enabled for all courses that use password authentication. If this is a string
# course name like 'admin', then two factor authentication is enabled only for
# that course. If this is an array of string course names, then two factor
# authentication is enabled only for those courses listed. This can also be set
# in a course's course.conf file. Note that only the values of 0 and 1 make
# sense there.
$twoFA{enabled} = 1;

# There are two methods that can be used to setup two factor authentication when
# a user signs in for the first time. The setup information can be emailed to
# the user, or can be directly displayed in the browser on the next page that is
# shown after password verification succeeds.
#
# If $twoFA{email_sender} is set, then the email approach will be used. In this
# case, after a user signs in and the password is verified, the user will be
# sent an email containing a QR code and instructions on how to set up a OTP
# generator app. This is probably a more secure way to set up two factor
# authentication, as it ensures the user setting it up is the correct user. Note
# that if a user does not have an email address, then the browser method below
# will be used as a fallback.
#
# If $twoFA{email_sender} is not set, then after a user signs in and the
# password is verified, the QR code, OTP link, and instructions will be
# displayed directly on the page in the browser. This is potentially less secure
# because a hacker could guess a username and password before a user has setup
# two factor authentication (particularly if the username and password are
# initially the same), and then the hacker would gain access to that user's
# account, and the actual user would be locked out. Note that you will need to
# use this option if your server can not send emails. Also note that no-reply
# addresses may be blocked by the email server or marked as spam. So it may be
# better to find a valid email address to use for this.
$twoFA{email_sender} = '';

# When a user signs in and enters the two factor authentication code, the user
# has the option to skip two factor verification on a given device for
# subsequent logins. That will only last for the amount of time set as the
# skip_verification_code_interval. By default this is set to one year. However,
# good security practices most likely recommend a shorter time interval for
# this. So change this value if you want to require a shorter and thus more
# secure time interval before users will need to enter the two factor
# authentication code again.
$twoFA{skip_verification_code_interval} = 3600 * 24 * 365;

################################################################################
# WeBWorK Caliper
################################################################################
Expand Down
71 changes: 70 additions & 1 deletion conf/localOverrides.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ $mail{feedbackRecipients} = [
# $permissionLevels{login} = "guest";

# The above code would give the permission to login to any user with permission
# level guest or higher.
# level guest or higher (which is the default).

# By default answers for all users are logged to the past_answers table in the database
# and the myCourse/logs/answer_log file. If you only want answers logged for users below
Expand Down Expand Up @@ -574,6 +574,75 @@ $mail{feedbackRecipients} = [
#$CookieLifeTime = 604800;
#$CookieLifeTime = "session";

################################################################################
# Two Factor Authentication
################################################################################

# The following variables enable two factor authentication and control how it
# works. Two factor authentication only applies to courses that use password
# authentication, i.e., the Basic_TheLastOption user authentication module
# without an external authentication approach (like LTI, CAS, Shibboleth, etc.).
# It is recommended that two factor authentication be enabled for all courses
# that use password authentication. It is extremely highly recommended that this
# be enabled for the admin course. Two factor authentication works with an
# authenticator app on a mobile device (such as Google Authenticator,
# Microsoft authenticator, Twilio Authy, etc.).

# $twoFA{enabled} determines if two factor authentication is enabled for a
# course. If this is set to 0, then two factor authentication is disabled for
# all courses. If this is 1 (the default), then two factor authentication is
# enabled for all courses that use password authentication. If this is a string
# course name like 'admin', then two factor authentication is enabled only for
# that course. If this is an array of string course names, then two factor
# authentication is enabled only for those courses listed. This can also be set
# in a course's course.conf file. Note that only the values of 0 and 1 make
# sense there.
#$twoFA{enabled} = $admin_course_id; # Use this at the very least.
#$twoFA{enabled} = [$admin_course_id, 'another_courseID', 'another_courseID_3'];

# There are two methods that can be used to setup two factor authentication when
# a user signs in for the first time. The setup information can be emailed to
# the user, or can be directly displayed in the browser on the next page that is
# shown after password verification succeeds.
#
# If $twoFA{email_sender} is set, then the email approach will be used. In this
# case, after a user signs in and the password is verified, the user will be
# sent an email containing a QR code and instructions on how to set up a OTP
# generator app. This is probably a more secure way to set up two factor
# authentication, as it ensures the user setting it up is the correct user. Note
# that if a user does not have an email address, then the browser method below
# will be used as a fallback.
#
# If $twoFA{email_sender} is not set, then after a user signs in and the
# password is verified, the QR code, OTP link, and instructions will be
# displayed directly on the page in the browser. This is potentially less secure
# because a hacker could guess a username and password before a user has setup
# two factor authentication (particularly if the username and password are
# initially the same), and then the hacker would gain access to that user's
# account, and the actual user would be locked out. Note that you will need to
# use this option if your server can not send emails. Also note that no-reply
# addresses may be blocked by the email server or marked as spam. So it may be
# better to find a valid email address to use for this.
#$twoFA{email_sender} = '[email protected]';

# When a user signs in and enters the two factor authentication code, the user
# has the option to skip two factor verification on a given device for
# subsequent logins. That will only last for the amount of time set as the
# skip_verification_code_interval. By default this is set to one year. However,
# good security practices most likely recommend a shorter time interval for
# this. So change this value if you want to require a shorter and thus more
# secure time interval before users will need to enter the two factor
# authentication code again.
#$twoFA{skip_verification_code_interval} = 3600 * 24 * 7;

# By default all users with the role of "student" or higher are required to use
# two factor authentication when signing in with a username and password. If
# you want to disable two factor authentication for students, but require it for
# instructors then set the permission level below to "login_proctor" (or
# higher).

#$permissionLevels{use_two_factor_auth} = "login_proctor";

################################################################################
# Searching for set.def files to import
################################################################################
Expand Down
7 changes: 7 additions & 0 deletions conf/webwork2.mojolicious.dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,10 @@ debug:
hardcopy:
# If 1, don't delete temporary files created when a hardcopy is generated.
preserve_temp_files: 0

# Set this to 1 to allow the html2xml and render_rpc endpoints to disable
# cookies and thus skip two factor authentication. This should never be enabled
# for a typical webwork server. This should only be enabled if you want to
# allow serving content via these endpoints to links in external websites with
# usernames and passwords embedded in them such as for PreTeXt textbooks.
allow_unsecured_rpc: 0
8 changes: 4 additions & 4 deletions htdocs/js/UserList/userlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@
e.stopPropagation();
show_errors(['export_file_err_msg'], [export_filename, export_select_target]);
}
} else if (action === 'delete') {
const delete_confirm = document.getElementById('delete_select');
} else if (action === 'delete' || action === 'reset_2fa') {
const action_confirm = document.getElementById(`${action}_select`);
if (!is_user_selected()) {
e.preventDefault();
e.stopPropagation();
} else if (delete_confirm.value != 'yes') {
} else if (action_confirm.value != 'yes') {
e.preventDefault();
e.stopPropagation();
show_errors(['delete_confirm_err_msg'], [delete_confirm]);
show_errors([`${action}_confirm_err_msg`], [action_confirm]);
}
}
});
Expand Down
18 changes: 13 additions & 5 deletions lib/WeBWorK.pm
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use WeBWorK::Debug;
use WeBWorK::Upload;
use WeBWorK::Utils qw(runtime_use);
use WeBWorK::ContentGenerator::Login;
use WeBWorK::ContentGenerator::TwoFactorAuthentication;
use WeBWorK::ContentGenerator::LoginProctor;

our %SeedCE;
Expand Down Expand Up @@ -90,12 +91,13 @@ async sub dispatch ($c) {
if ($c->current_route =~ /^(render_rpc|instructor_rpc|html2xml)$/) {
$c->{rpc} = 1;
$c->stash(disable_cookies => 1) if $c->current_route eq 'render_rpc' && $c->param('disableCookies');
$c->stash(disable_cookies => 1)
if $c->current_route eq 'render_rpc' && $c->param('disableCookies') && $c->config('allow_unsecured_rpc');
# This provides compatibility for legacy html2xml parameters.
# This should be deleted when the html2xml endpoint is removed.
if ($c->current_route eq 'html2xml') {
$c->stash(disable_cookies => 1);
$c->stash(disable_cookies => 1) if $c->config('allow_unsecured_rpc');
for ([ 'userID', 'user' ], [ 'course_password', 'passwd' ], [ 'session_key', 'key' ]) {
$c->param($_->[1], $c->param($_->[0])) if defined $c->param($_->[0]) && !defined $c->param($_->[1]);
}
Expand Down Expand Up @@ -268,9 +270,15 @@ async sub dispatch ($c) {
# If the user is logging out and authentication failed, still logout.
return 1 if $displayModule eq 'WeBWorK::ContentGenerator::Logout';
debug("Bad news: authentication failed!\n");
debug("Rendering WeBWorK::ContentGenerator::Login\n");
await WeBWorK::ContentGenerator::Login->new($c)->go();
if ($c->authen->session->{two_factor_verification_needed}) {
debug("Login succeeded but two factor authentication is needed.\n");
debug("Rendering WeBWorK::ContentGenerator::TwoFactorAuthentication\n");
await WeBWorK::ContentGenerator::TwoFactorAuthentication->new($c)->go();
} else {
debug("Bad news: authentication failed!\n");
debug("Rendering WeBWorK::ContentGenerator::Login\n");
await WeBWorK::ContentGenerator::Login->new($c)->go();
}
return 0;
}
}
Expand Down
Loading

0 comments on commit d497e06

Please sign in to comment.