Community contributed extensions

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 @\@[email protected] 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 @\@[email protected] 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:

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.