Shibboleth authentication

From EPrints Documentation
Revision as of 10:41, 28 April 2008 by Kiz (talk | contribs) (Shibboleth Authentication)
Jump to: navigation, search

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.