Real-world example of adding SAML authentication to a JBoss application

What is SAML?

SAML stands for Security Assertion Markup Language. It's an XML-based markup language and also an open security protocol standard. The most important feature of this protocol is Web Browser Single Sign-On (SSO.) SSO allows users to login once and never be prompted for login/password later.

In this post we'll describe how to add SAML support to existing web application with the bare minimum of modifications to the application code itself.

Shibboleth

While it's not impossible to implement the SAML protocol standard manually, it's much easier (and much less time-consuming) to take a ready to use solution, such as Shibboleth, and this is what we are going to do: we'll integrate an existing JBoss application, running behind an Apache web server, with Shibboleth SAML2 authentication.

Installing Shibboleth

The first step would be to download and install Shibboleth. Depending on your system, you might need to build it from source (or source RPMs,) but on Debian it comes pre-packaged:

# apt-get install libapache2-mod-shib2

This will download and install Shibboleth authentication Apache module as well as shibd daemon, which handles the details of SAML protocol.

Currently, Debian ships with 2.3 series of Shibboleth, while the newest released version is 2.4. There's not much difference between the two versions (although the newer one allows for more compact configuration file.) If you need the 2.4 version, you'll have to install it in some more involved way. Refer to the installation docs here: https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPLinuxInstall

Configuring Shibboleth

Generally, Shibboleth works out of the box, but you need to provide some minimal configuration for it to do anything sensible.

Note: when installing from Source RPMs on rpm-based systems, you might need to adjust some build parameters or make /etc/XML-Tooling a symlink to /etc/shibboleth, since different parts of it seem to look for configuration files in different directories (I don't recall the exact name for the first dir, but strace is your friend if shibd won't start and print some cryptic errors on console.)

Service Provider configuration

For this scenario we will be configuring Shibboleth as a SAML Service Provider (the authentication side which provides restricted access to the resources and asks SAML Identity Provider for user access authorization.) Start by editing /etc/shibboleth/shibboleth2.xml and replace the default value of the entityID attribute in ApplicationDefaults element with some string which identifies your Service Provider in a unique way.

Typically a URL of the form http://my.service-provider.com/shibboleth can be used here (where /shibboleth URI not necessarily represents a resource which can be actually fetched using HTTP, it's just a common naming convention.)

In order to present your Service Provider with a security certificate you'll need to add/edit the CredentialResolver configuration element. On Debian (with openssl package installed) you may use the auto-generated "SnakeOil" certificate and private key, like this (for testing purposes only):

<CredentialResolver type="File"
    key="/etc/ssl/private/ssl-cert-snakeoil.key"
    certificate="/etc/ssl/certs/ssl-cert-snakeoil.pem"/>

Be sure to replace this with the real certificate and key once you're done with your testing.

Note: you'll need to restart shibd daemon for this change to take effect, since the above private key is normally not world-readable. Other changes to Shibboleth configuration take effect w/o a restart generally.

Identity Provider configuration

Now, any Service Provider (SP) in SAML must know to which Identity Providers (IdP) it should talk when authorizing or denying access to any particular restricted resource. To give your SP this information you'll need to edit entityID attribute of an appropriate SessionInitiator element (pre-2.4 versions) or of a SSO element (versions 2.4 and higher.) The SSO element in the newer versions' configuration is a nice shortcut to replace the whole series of configuration elements required in earlier versions (those, on the other hand, provide more flexibility.)

After putting the appropriate entityID for the IdP you are going to use for user authentication, you also need to add the IdP metadata file to the configuration, using an element like this:

<MetadataProvider type="XML" file="idp-metadata.xml"/>

Where idp-metadata.xml is the XML-file containing SAML metadata for the IdP being used. You should contact your IdP to obtain this file (if you don't have this information at this point, refer to the "Testing" section below for testing options.) There is also a possibility to refer to IdP metadata by using a URL to it. The Shibboleth daemon will poll the URL periodically to check for any update on the metadata. Refer to Shibboleth documentation on MetadataProvider configuration element for details.

Apache configuration

The above sections cover the Shibboleth configuration. However, this doesn't automagically adds SAML to your application... yet.

Shibboleth comes with a standalone daemon which actually handles the authentication and a web-server module, which observes client requests and talks to the Shibboleth daemon to deny or authorize the access. In this post we will be configuring Apache web server, please refer to Shibboleth documentation for use with different web servers.

To enable Shibboleth authentication for /secure URI on your server, add the following block to your VirtualHost:

<Location /secure>
    AuthType shibboleth
    ShibRequestSetting requireSession 1
    Require valid-user
</Location>

Or use / location to restrict access to your site as a whole. Restart or reload Apache to make the change take it's effect (by the way, Shibboleth daemon doesn't require a restart after modification of it's configuration files: it reloads them automatically.)

Testing

At this point your Shibboleth and Apache installations are configured and now it's time to test the setup. However, sometimes you won't have the Identity Provider metadata or you just want to perform some internal testing before approaching the IdP.

You may try setting up a local IdP using the same Shibboleth installation for your test. Also there are some public online IdPs which you may use to test your newly setup SP, but I've found that installing a different SAML implementation for testing IdP works the best. First, testing on a different implementation gives your extra confidence that you've got it right, as opposed to testing with the same implementation as that of SP. Second, a locally installed solution allows for easier debugging, as opposed to using established online services.

I've found that SimpleSAMLphp works really well for this purpose and requires minimum of configuration. So you might want to install it either on the same host or use a separate host for your testing IdP.

After configuring your testing IdP you'll need to add your SP metadata, so the IdP may verify the authentication requests coming from your SP. The SP metadata with Shibboleth is not stored in a file, but generated on request to http://localhost/Shibboleth.sso/Metadata (by default, from localhost only.) SimpleSAMLphp provides a web form to parse the XML metadata into it's internal format (PHP code actually) which you may paste into the IdP configuration file afterwards (typically, simplesamlphp/metadata/saml20-sp-remote.php.)

When this is done you are ready to test. Try accessing a restricted URL on your Service Provider site and watch you being redirected to your testing Identity Provider for authentication. After filling the login form correctly, you should be redirected back to your SP site.

However, at this point we didn't make any change to our application, so it won't notice any difference when we visit the restricted URL and are already authenticated with the IdP.

Accessing authentication information in the application

Shibboleth passes the authentication information to the web application in form of CGI environment variables ($_SERVER array variable in PHP.) The sign of an established SAML session is presence of Shib-Session-ID environment variable.

However, if you use JBoss application server your code lives in a different process and has no direct access to the Apache server environment. To pass the required variables from Apache to JBoss you'll need to add the following mod_jk configuration directive:

JkEnvVar Shib-Session-ID

After that, you may get the value of this variable using request.getAttribute("Shib-Session-ID") in your Java or JSP code.

Accessing user identity information

Now you may tell apart SAML-authenticated access to your application from the non-authenticated. But session id is not suitable for identifying actual users, so how would you tell different users apart?

Here's where additional Shibboleth configuration files come into play. First, you might want to check the shibd.log file after successfully authenticating with your IdP. It will list the user identity attributes extracted from the IdP SAML response (such as name, email, affiliation, etc.)

If you don't see the expected attributes in the log, you might need to edit attribute-map.xml configuration file to add some Attribute elements. If you don't know which attributes to expect, it might be helpful to increase logging level for your SP or IdP to capture the exact SAML response XML text being sent and look for saml:Attribute elements which might look familiar. For example, you might want to add something like this to the attribute map file:

<Attribute name="username" id="SAML-UserName"/>

This will pass the value of SAML username attribute in the SAML-UserName environment variable (remember to add appropriate mod_jk directives if you need to pass them further to JBoss.)

As a special case, the user identity (NameID) attributes extraction require special configuration syntax. For example:

<Attribute name="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
              id="SAML-UserName">
    <AttributeDecoder xsi:type="NameIDAttributeDecoder" formatter="$Name"
              defaultQualifiers="false"/>
</Attribute>

Refer to Shibboleth documentation for details.

Performing sign-on using SAML user id

Now you should be able to identify a SAML-authenticated user. Depending on your application details you might need to find the corresponding user record in your application's authentication structures or add the new user unconditionally (after all you trust the IdP that the user is the one he pretends to be.)

Note: this is the first place where you'll need to touch your application code. All of the above only requires external configuration changes.

Handling Single Logout

If you successfully managed to handle Single Sign-On using the above information, you'll likely want to add handling for Single Logout, so that user may log out of your Service Provider as well as Identity Provider.

Single Logout in Shibboleth is handled by /Shibboleth.sso/Logout URI by default. You might want to redirect user to that URI after logging out of your application (local logout.) This is the second place where your application code might need to be modified.

Conclusion

In this post we've shown how you can add SAML-based authentication to your application with the minimum of changes to the application code, with the use of Shibboleth SAML authentication engine.