Shibboleth authentication
Contents
Shibboleth Authentication
TIMTOWTDI (There Is More That One Way To Do It)
This is the system I implemented for using with the UK Access management Federation (which is a Shibboleth implimentation).
How Shibboleth Works
The UK-AMF authentication is reasonably complicated:
- The user is directed to a Shibboleth authentication point, supplying a Context URL.
- Once the user has been authenticated, the Context URL is called.
- The Context URL processes the data from the authentication point and returns a URL with an initiated session (however (if!) the web site uses session management)
- The user's browser is redirected to that URL (effectively by their own browser)
How to make EPrints do this
There are two problems when implimenting this under EPrints:
- EPrints does it's session management in a cookie not via a URL parameter
- It is desirable to leave as much of that session management as standard as possible.
- The user Authentication (and Identification) and the actual Login process are done in different places, with multiple HTTP page calls.
- Passing user identification details as parameters in an insecure http request means that someone can fake authenticated credentials and thus get access when they shouldn't.
What I have implemented is something similar to the standard eprints login_ticket system: the context URL phase sets some entries in an authorisation table, and then the main EPrints code just pulls the identity parameters from the table.
Adding a table to the database
In perl_lib/EPrints/Database.pm
add the following code:
In sub create_archive_tables
, add
$success = $success && $self->create_auth_tickets_table();
and then add the following two mathods
# $db->create_auth_tickets_table() # # create the auth_tickets table. sub create_auth_tickets_table { my( $self ) = @_; my $sql = "CREATE TABLE auth_tickets ( code CHAR(32) NOT NULL, puid VARCHAR(255), orgid VARCHAR(64), expires INTEGER, primary key( code ) )"; return $self->do( $sql ); } # $db->get_auth_puid( $code ) # # return the puid, if any, associated with the given auth ticket code. sub get_auth_puid { my( $self, $code ) = @_; my $sql; # clean up old tickets $sql = "DELETE FROM auth_tickets WHERE ".time." > expires"; $self->do( $sql ); $sql = "SELECT puid,orgid FROM auth_tickets WHERE code='".prep_value($code)."'"; my $sth = $self->prepare( $sql ); $self->execute( $sth , $sql ); my( $puid, $orgid ) = $sth->fetchrow_array; $sth->finish; return ($puid, $orgid); }
The Authentication routine
I have all my authentication routines in the same place (perl_lib/EPrints/myCode/Autho.pm
) which makes it available in multiple places.
The Shibboleth routine is:
sub shibb_autho { my ($session) = @_; my $authentication_point = 'https://Your.Shibb.Authentication.Point/with/path'; my $secret_code; my $orgName; my $puid; if (! $session->have_parameters) { my $args = $session->{'request'}->args(); $args =~ /s=(.*?)[&;\b]/; $secret_code = $1; # $r->param('session'); } else { $secret_code = $session->param('s'); } my $ath_returl = $session->get_full_url; unless ($secret_code) { redirect( url => '$authentication_point', params => { context => $ath_returl, service => 'myServiceCode', } ); exit; } ($puid, $orgName) = $session->{database}->get_auth_puid( $secret_code ); return unless $puid; my %return_hash = ( userID => $puid, orgName => $orgName, ); return \%return_hash; }
Looking at the code, it breaks down into two main parts:
- If there is no secret code then we assume the user has not been via the authentication point, and send them there
- If we have a secret code then we use it as the index for the authentication table, and pull out the puid.
- if we don't get a puid, return (as a failure)
- if we have got a puid, build a return-hash and send it back.