NAME

Plack::Auth::SSO::Shibboleth - implementation of Plack::Auth::SSO for Shibboleth

SYNOPSIS

DESCRIPTION

This is an implementation of Plack::Auth::SSO to authenticate behind a Shibboleth Service Provider (SP)

It inherits all configuration options from its parent.

CONFIG

request_type

* "env": Shibboleth SP sends attributes using environment variables (CGI and FCGI)

* "header": Shibboleth SP sends attributes using headers (proxy)

Default is "env"

shib_session_id_field

Field where Shibboleth SP stores the session id.

Default is "Shib-Session-ID"

shib_application_id_field

Field where Shibboleth SP stores the application id.

Default is "Shib-Application-ID"

uid_field

Field to be used as uid

Default is "eppn"

info_fields

Fields to be extracted from the environment/headers

auth_sso output

{

package => "Plack::Auth::SSO::Shibboleth",

package_id => "Plack::Auth::SSO::Shibboleth",

#configured by "uid_field"
uid => "<unique-identifier>",

#configured by "info_fields". Empty otherwise
info => {
    attr1 => "attr1",
    attr2 => "attr2"
},

#Shibboleth headers/environment variables
extra => {
    "Shib-Session-Id" => "..",
    "Shib-Application-Id" => "..",
    "Shib-Identity-Provider" => "https://path.to/shibboleth./idp",
    "Shib-Authentication-Instant" => "",
    "Shib-Authentication-Method" => "POST",
    "Shib-AuthnContext-Class" => "..",
    "Shib-AuthnContext-Decl" => "..",
    "Shib-Handler" => "..",
    "Shib-Session-Index" => ".."
    "Shib-Cookie-Name" => ".."
},

#We cannot access the original SAML response, so we rely on the headers/environment
response => {
    content_type => "application/json",
    content => "<headers/environment serialized as json>"
}
}

GLOBAL SETUP

This module does not do what it claims to do: authenticating the user by communicating with an external service.

The real authenticating module lives inside the Apache web server, and is called "mod_shib".

That module intercepts all requests to a specific path (e.g. "/auth/shibboleth"), authenticates the user, and, when done, sends the requests to the backend application. As long as a Shibboleth session exists in mod_shib, the request passes through.

That backend application merely receives the end result of the authentication: a list of attributes. The original SAML response from the Shibboleth Identity Provider is not sent.

There are two ways to transfer the attributes from mod_shib to the application:

* the application lives inside Apache (CGI, FCGI). The attributes are sent as environment variables. This is the default situation, and the most secure.

* the application is a separate server, and Apache merely a proxy server. The attributes are sent as headers. This is less secure.

cf. <https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPAttributeAccess>

This module merely convert these attributes.

SETUP BEHIND PROXY

plack application

use Catmandu::Sane; use Catmandu::Util qw(:is); use Plack::Auth::SSO::Shibboleth; use Plack::Builder; use Plack::Session; use JSON;

my $uri_base = "https://example.org";

builder {

enable "Session",

#mod_shib should intercept all requests to this path
mount "/auth/shibboleth"  => Plack::Auth::SSO::Shibboleth->new(
    uri_base => $uri_base,
    authorization_path => "/authorize",
    uid_field => "uid",
    request_type => "header",
    info_fields => [qw(mail organizational-unit-name givenname sn unscoped-affiliation entitlement persistent-id)]
)->to_app();

mount "/authorize" => sub {

    my $env = shift;
    my $session = Plack::Session->new($env);

    #already logged in. What are you doing here?
    if ( is_hash_ref( $session->get("user") ) ) {

        return [
            302,
            [ Location => "${uri_base}/authorized" ],
            []
        ];

    }

    my $auth_sso = $session->get("auth_sso");

    #not authenticated yet
    unless($auth_sso){

        return [
            302,
            [ "Location" => "${uri_base}/" ],
            []
        ];

    }

    $session->set("user",{ uid => $auth_sso->{uid}, auth_sso => $auth_sso });

    [
        302,
        [ Location => "${uri_base}/authorized" ],
        []
    ];

};
mount "/authorized" => sub {
    state $json = JSON->new->utf8(1);

    my $env = shift;
    my $session = Plack::Session->new($env);
    my $user = $session->get("user");

    #not logged in
    unless ( is_hash_ref( $user ) ) {

        return [
            401,
            [ "Content-Type" => "text/plain" ],
            [ "Forbidden" ]
        ];

    }

    #logged in: show user his/her data
    [
        200,
        [ "Content-Type" => "application/json" ],
        [ $json->encode( $user ) ]
    ];

};

};

httpd.conf

NameVirtualHost *:443 <VirtualHost *:443 >

ServerName example.org

#shibd is a background service, so it needs to know the domain and port
UseCanonicalName on
UseCanonicalPhysicalPort on

#configure SSL
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite "ALL:!ADH:!EXP:!LOW:!RC2:!SEED:!RC4:+HIGH:+MEDIUM HIGH:!SSLv2:!ADH:!aNULL:!eNULL:!NULL !PSK !SRP !DSS"
SSLCertificateFile /etc/httpd/ssl/server.pem
SSLCertificateKeyFile /etc/httpd/ssl/server.key
SSLCACertificateFile /etc/httpd/ssl/server.pem

#do not proxy Shibboleth paths
ProxyPass /shibboleth-sp !
ProxyPass /Shibboleth.sso !

#proxy all requests to background Plack application
ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/

#all request to /auth/shibboleth should be intercepted by mod_shib before
#sending to background plack application
<Location /auth/shibboleth>

  AuthName "shibboleth"
  AuthType shibboleth
  Require valid-user
  ShibRequestSetting requireSession true
  ShibRequestSetting redirectToSSL 443

  #necessary to send the attributes in the headers
  ShibUseHeaders On
</Location>

#Path to metadata.xml
Alias /shibboleth-sp /var/www/html/shibboleth-sp

#handler for Shibboleth Service Provider
<Location /Shibboleth.sso>
  SetHandler shib-handler
  ErrorDocument 403 /public/403.html
</Location>

ProxyRequests Off
<Proxy *>
  Order Deny,Allow
  Allow from all
</Proxy>

</VirtualHost>

AUTHOR

Nicolas Franck, <nicolas.franck at ugent.be>

SEE ALSO

Plack::Auth::SSO