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
- error_path
-
This option is inherited by its parent class Plack::Auth::SSO, but cannot be used unfortunately
because an SP will never allow an invalid request to be passed to the backend. This should be configured in
/etc/shibboleth/shibboleth2.xml ( cf. https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPErrors ).
- 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-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
strict;
use
Plack::Builder;
use
Plack::Session;
use
JSON;
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>
LOGGING
All subclasses of Plack::Auth::SSO use Log::Any to log messages to the category that equals the current package name.
AUTHOR
Nicolas Franck, <nicolas.franck at ugent.be>