Shibboleth authentication module
The Shibboleth module provides integration with the Shibboleth authentication protocol. The Shibboleth protocol is a distributed single sign on service across multiple organizational boundaries popular in higher education. The protocol is divided into three components: Service Provider (SP), Identity Provider (IDP), and Discovery Service (DS/WAYF). This module facilitates your Play! application becomes a Service Provider.
Requirements
In order to use the shibboleth module you must proxy your Play! application behind a Shibboleth capable web server such as Apache, Lighttpd, or IIS. Install and configure Shibboleth on the proxy web server, then enable this module as described below.
This module offers a both "restricted session" and a "lazy session" (sometimes refereed to as "passive session") model. Under a restricted session model your application will only receive HTTP requests from users who have successfully authenticated. No anonymous or unauthenticated users will be allowed to access your application. Under the lazy model both anonymous and authenticated requests will be handled by your application. This allows for anonymous users to browse your application. Then your application can decide to initiate Shibboleth authentication when it makes sense (i.e. the user click on he login link).
Enable the Shibboleth module for the application
In the /conf/dependencies.yml
file, enable the this module by adding a line after require:
require:
- play -> shibboleth
Import Shibboleth routes
In the conf/routes
file, import the default module routes by adding the line shown below. You may also define your own routes if you wish, Shibboleth requires a login, logout, and authentication route.
#Import Shibboleth routes
* / module:shibboleth
Configure Shibboleth authentication in Play
Shibboleth requires some configuration before it can be used at a practical minimum you must provide some shib.attribute.<Attribute Name> = <HTTP Header>
attribute mappings. These map the Shibboleth attributes to session attributes that will be available to your application. You can use the shib.require
parameter to indicate which of these attributes are required for successful authentication. You can also override the onAuthenticated()
hook described below to implement more complex attribute mapping strategies.
# Shibboleth attributes
shib.require = email, firstName, lastName
shib.attribute.email = SHIB-email
shib.attribute.firstName = SHIB-givenName
shib.attribute.lastName = SHIB_sn
...
shib.attribute.<Attribute Name> = <HTTP Header>
Beyond the attributes configuration you will need to add additional configuration based upon session model being used.
Restricted Sessions
Under this session model your application will only receive authenticated requests. If a fresh user visits your application Shibboleth will start the authentication process and only after a user has successfully authenticate will the request be sent to your application. To use this model you need to be sure to set ShibRequireSessions Off
in your Apache configuration (or equivalent for your web server) so that the web server knows to enforce this model. Next you will need to tell the Play Shibboleth module to turn off all Shibboleth initiators. This is the default values.
# Turn off Shibboleth initiators
shib.login = false
shib.logout = false
Lazy/passive Sessions
Under this session model your application will receive all requests authenticated or not. When your application detects that the user needs to login (i.e. clicking the login link) you need to redirect to the Shibboleth.login() controller to initiate the Shibboleth authentication transaction. In order to support this you need to provide the the Play Shibboleth module with the initiator urls to use, typically /Shibboleth.sso/login
or /Shibboleth.sso/logout
, starting the login / logout process. You may use fully qualified urls (i.e. http://
) or absolute urls (just /
). However if your application accepts both protocols (http and https) then it is encouraged to use fully qualified urls so that you can specify the Shibboleth transaction to occur over SSL. The return parameters (shib.login.return
and shib.logout.return
) determine where the user will be sent after successfully completing the action. In the case of loginig in, the user will be returned to the URL they were attempting to access (i.e. a controller using the @\@With@ annotation). If there was interrupted url then the user will be sent to the default return url defined in shib.login.return
.
There is a debate about whether it is positive to even provide a logout function for Shibbilized applications. Users may not necessarily be logged out of any other applications that they may have previously logged in. This could potentially cause confusion for users who thought that they have successfully loged out. By default the module disables the logout feature, however you may enable it by specifying shib.logout = true
# Shibboleth login initiator
shib.login = true
shib.login.url = https://localhost/Shibboleth.sso/Login
shib.login.return = http://localhost/
# Shibboleth logout initiator (default false)
shib.logout = true
shib.logout.url = https://localhost/Shibboleth.sso/Logout
shib.logout.return = http://localhost/
Configure Apache/Lighttpd
Apache set-up
Configure Apache’s virtual host or other directive to use shibboleth. The configuration provided below assumes lazy authentication, i.e. users will be able to access your application without authenticating. If you want only authenticated users accessing the application then set ShibRequireSessions On
and be sure to turn off shib.login
in your application.conf as shown above. When defining the proxy parameters it is highly advantageous to also use ProxyPreserveHost on
so that your Play application knows what external host the application is being accessed from.
AuthType shibboleth
ShibURLScheme https
ShibRequireSessions Off
ShibUseHeaders On
Require shibboleth
Lighttpd set-up
The module developers have not tested this configuration, but it should work. If you use lighttpd in your configuration let us know.
server.name = "your_server_name"
server.document-root = "/servers/tags/www/"
fastcgi.server = (
"/Shibboleth.sso" => (("socket" => "/tmp/fcgi-resp.sock",
"bin-path" => "/servers/sapo-sp/lib/shibboleth/shibresponder",
"check-local" => "disable",
"mode" => "responder")),
"/" => (("socket" => "/tmp/fcgi-auth.sock",
"bin-path" => "/servers/sapo-sp/lib/shibboleth/shibauthorizer",
"check-local" => "disable",
"mode" => "authorizer"))
)
Using Shibboleth in your application
Controller Annotation
The Shibboleth module works in a very similar manner to the default Security
module. To protect a controller requiring authentication use the @\@With@ annotation. For example:
@With(Shibboleth.class)
public class Application extends Controller {
public static void index() {
render();
}
}
In addition to using the controller.shib.Secure
for restricting access to particular controllers you can also use it to customize how Shibboleth interacts with your application. Create a controller that extends the Shibboleth Secure class. When you do this you can add specific customization checks, for example:
@With(Shibboleth.class)
public class Application extends Controller {
...
@Check("isAdmin")
public static void modify() {
render();
}
}
By default the Check
annotation will always authorize all checks. You will need to customize the behavior by creating a controller in your application which extends Shibboleth’s Security class.
package controllers;
public class Security extends controller.shib.Security {
...
static boolean check(String profile) {
User user = User.find("byEmail", session.get("email")).first();
if ("isAdmin".equals(profile)) {
return user.admin;
} else {
return false;
}
}
}
Other methods that can be customized are:
- onAuthenticated() - This hook is called just after a user has successfully authenticated. Use the hook to further process the attributes received or implement complex attribute strategies. If you want to cancel the authentication call the
onAttributeFailure()
hook directly. - onDisconnect() - This hook is called just before a user is about to logout. The session still holds all the identifying information about the user.
- onDisconnected() - This hook is called just after a user has logged out and the session has been cleared.
- onCheckFailed() - This hook is called when a user has failed a
Check
annotation check for a particular profile. - onAttributeFailure() - This hook is called when a user has failed to supply the minimum configuration of attributes.
Tip
Shibboleth attributes may contain multiple values when this happens Shibboleth will encode all the attribute values separated by a semicolon, and semicolons will be escaped with a \
character. You can use the static Shibboleth.split(attribute)
method to easily split the attribute into it’s multiple components.
Template tag
You can also use the shibboleth.check
tag for authorization checks in templates. This is useful for conditionally displaying user-interface controls for operations that are protected by controller annotations. For example:
#{shibboleth.check 'administrator'}
...some administrative stuff....
#{/shibboleth}
The tag only renders its body for authorized users, so the administrative stuff is only displayed when the user is an administrator.
Testing with Shibboleth
Using a fully functional Shibboleth implementation for testing is often times impractical. This module allows you to mock Shibboleth attributes so that in a testing environment you do not need to setup a Shibbolized proxy webserver. First turn on the mock implementation with shib = mock
then provide a set of attributes to use when mocking Shibboleth: shib.mock.<HTTP Header> = <Header Value>
. When any user attempts to login to your application with the Shibboleth mock turned on instead of using Shibboleth these attributes provided here will be assumed.
%test.shib = mock
%test.shib.mock.SHIB_email = [email protected]
%test.shib.mock.SHIB_givenName = Bob
%test.shib.mock.SHIB_sn = Smith
...
%test.shib.mock.<HTTP Header> = <Header Value>
When developing functional application tests it is often usefull to authenticate as different users for individual test cases. The method described above does not allow this, however you can override these settings for a particular test case by using the MockShibboleth
class directly. For example:
@Test
public void testShibbolethAuthentication() {
MockShibboleth.removeAll();
MockShibboleth.set("SHIB_email","[email protected]");
MockShibboleth.set("SHIB_givenName", "Some");
MockShibboleth.set("SHIB_sn", "One");
final String LOGIN_URL = Router.reverse("shib.Shibboleth.login").url;
Response response = GET(LOGIN_URL,true);
assertIsOk(response);
assertTrue(response.cookies.get("PLAY_SESSION").value
.contains("[email protected]"));
}
@AfterClass
public static void cleanup() {
MockShibboleth.reload();
}
More examples are provided in the Sample Shibboleth Application provided with the module.