Authentication
Contents
Authentication
By default, WeBWorK manages how users (students and instructors) authenticate to be allowed access to resources. There are a number of modules that allow WeBWorK to defer to another authentication source, e.g., so that it can use a central login service on a campus. The different implementations of such external authentication resources are indicated below. The remainder of this document describes generally how authentication is managed in WeBWorK, to inform how the existing modules work and provide some background information about how to set up a new one.
Existing Authen Modules
There are currently several WeBWorK authentication modules to allow it to use an external authentication system:
- Authen::Cosign The Authen::Cosign module is designed to allow authentication with a system that manages authentication by way of web-server level configuration. That is, in this case you must be able to tell your apache server that given directories (that is, the course directories) are protected by your authentication system, and then the web server takes care of the redirect that is required for the user to log in. Then if the user ever gets to WeBWorK we know that s/he has authenticated and have only to check that the user is in the correct course roster. This is explained in the course of
- Authen::LDAP This is "similar" to Cosign authentication, but in this case we have to do more work to figure out if the user is valid. This is illustrated in the explanation below.
- Authen::Moodle A module to allow authentication through Moodle.
WeBWorK's Authentication Process
WeBWorK controls authentication to the homework system in the Authen.pm module. To verify that a user is authorized to access the WeBWorK system, it the module checks, in order
$self->get_credentials; $self->check_user; $self->verify_normal_user;
or, if the user is a practice (guest) user, the last is replaced with
$self->verify_practice_user;
These methods, respectively, get the user's login name and authentication credentials, then check that the user is a valid WeBWorK user, and finally, checks that the authentication credentials match those for the user.
The verify_normal_user
routine is also configured to easily allow separate site authentication: in pseudo-code, it checks authentication with
if ( $self->valid_webwork_session() ) { return 1; } else { if ( $self->checkPassword( $user_id, $password ) ) { $self->create_session(); return 1; } elsif ( $self->can( site_checkPassword ) && $self->site_checkPassword( $user_id, $password ) ) { $self->create_session(); return 1; } else { return 0; } }
Accordingly, if we want to develop a new authentication module, we need to replace one or more of these methods in a subclass of the Authen module. This is considered below in the examples.
Examples
Cosign
Cosign (Collaborative single-sign on) is an open source project originally designed to provide the University of Michigan with a secure single sign-on web authentication system. It is part of the National Science Foundation Middleware Initiative (NMI) EDIT software release.
Cosign runs as an apache module (as does WeBWorK), so that authentication through Cosign is based on the location that is being accessed. This means that if we configure the apache server to recognize a WeBWorK course as being Cosign protected the web server will automatically redirect the user to the Cosign login if the user isn't authenticated with Cosign, and won't allow the user to access the resource if the user isn't authenticated. This makes using Cosign pretty easy: we tell our webserver to protect a WeBWorK course with Cosign, and then if the user actually gets to the course we know that they've already successfully authenticated against the central login server.
Thus, in our Authen::Cosign.pm module, we have only two small changes:
- In
$self->get_credentials
we have to get the user's login information from Cosign, not from a WeBWorK form; - In
$self->site_checkPassword
we just return 1 (true), because we know that if the user checked correctly incheck_user
and we've entered the WeBWorK system, then the user has both successfully authenticated against the central server and is a valid WeBWorK user.
We don't have to change the checkUser
method, but we do have to be sure that the classlist in the WeBWorK class is kept up-to-date.
get_credentials
The simplest get_credentials
method for Cosign authentication gets the user's login name from the environment (Cosign assures that this is set), and then forces verify_normal_user
to fall through to the site_checkPassword
routine:
sub get_credentials { my ($self) = @_; my $r = $self->{r}; my $ce = $r->ce; my $db = $r->db; # # Cosign makes sure that the environment variable REMOTE_USER # is set for us. if ( defined( $ENV{REMOTE_USER} ) ) { $self->{user_id} = $ENV{REMOTE_USER}; $self->{r}->param("user", $ENV{REMOTE_USER}); } else { return 0; } # we set the external auth parameter so that Login.pm knows # what error to return if there's a check_user failure. $self->{external_auth} = 1; # # set other parameters to force a password check that will # call site_checkPassword $self->{session_key} = undef; $self->{password} = 'youWouldNeverPickThisPassword'; $self->{credential_source} = params; # return 1; }
site_checkPassword
Because of the availability of the site_checkPassword
function in verify_normal_user
, we just have to provide such a routine:
sub site_checkPassword { my ( $self, $userID, $clearTextPassword ) = @_; # if we got here, we know we've already successfully authenticated against # Cosign! return 1; }
The actual code in Authen::Cosign.pm is slightly more complicated, but not much; see the module in the CVS for comparison.
LDAP
LDAP is a protocol to allow networked access to directory information (e.g., usernames and passwords). Therefore, to use it to authenticate WeBWorK users, we have to replace the checkPassword
or site_checkPassword
routines to query the LDAP server to find if the user is valid.
The simplest LDAP authentication system would therefore not have to change either of the get_credentials
or check_user
methods, and could leave checkPassword
as well as long as the LDAP password didn't match the password in WeBWorK. This implementation would have
sub site_checkPassword { my ( $self, $userID, $clearTextPassword ) = @_; if ( $self->ldap_authen_uid( $userID, $clearTextPassword ) ) { return 1; } else { return 0; } }
and then the routine ldap_authen_uid
would check against the LDAP server. Alternately, we might want to first authenticate against LDAP, and if that fails, authenticate against the standard WeBWorK passwords. This requires changing checkPassword
as well, as suggested by the following example.
sub checkPassword { my ( $self, $userID, $clearTextPassword ) = @_; if ( $self->ldap_authen_uid( $userID, $clearTextPassword ) ) { return 1; } else { return $self->SUPER::checkPassword($userID, $clearTextPassword); } }
Note that in this case we can omit the site_checkPassword
method. If we didn't want to authenticate against the WeBWorK database, the call to $self->SUPER::checkPassword
could be replaced with a return 0
to force failure in that case.
In either case, the ldap_authen_uid
routine must check the user with the LDAP server. For the actual (somewhat more complicated) implementation of checkPassword
, and the code for ldap_authen_uid
, see the module in the CVS.