IMPORTANT NOTE: This article describes a very early, probably the first attempt to use SAML-based authentication in a Seam application. For people who are interested in history, it can be interesting. However, developers that are just interested in how to add SAML or OpenID support to their Seam application, should have a look at these articles instead:
(by Marcel Kolsteren, The Netherlands)
This article discusses ways in which Seam web applications can make use of the SAMLv2-based SSO support available in JBoss Identity. It presents a working example of SSO for two Seam-based applications.
HTTP Redirect Profile
JBoss Identity Federation (part of JBoss Identity) supports various SAMLv2 profiles. We will only consider the profile where the service provider (SP) and identity provider (IP) communicate via the browser of the user, using HTTP redirects.
The JBoss Identity Federation package supplies Tomcat valves that can handle the protocol at the SP and IDP side. At the SP side, the web application specifies FORM-based authentication (in the web.xml), and configures the valve (in context.xml). When the browser requests a secured page, the servlet container would normally load a JSP-page, which presents a login form. But this doesn't happen, because the SP-valve takes over: it redirects the browser to the IDP. After the user has been authenticated by the IDP, the SP-valve processes the SAML authentication response. The user will be silently logged in.
In an ideal world, we would have Seam support in JBoss Identity Federation, as a separate binding. However, until such a binding is available, the SP-valves can be used in combination with Seam. The first step is to secure the pages at the servlet level, just in the way described above. This would already result in a redirect to the IDP, and a redirect back, which is processed by the SP-valve, logging the user on at servlet level. The second step is to hand over the authenticated user to the Seam session. In other words, the user needs to be pushed into the identity component of Seam. Seam contains an extension point for pluggin in custom authentication, by specifying an authenticate-method or jaas-config-name in the security-identity element in components.xml. Unfortunately, this only allows us to change the way that the user's credentials are verified, whereas we would like to change the retrieval of the credentials as well. So we seem to be committed to more intrusive and less documented ways of customizing. We describe two methods for doing so.
Binding method 1: Replace Seam's identity Component
This method is inspired by Gerald Anderson's post on the Seam forum. We replace the Seam identity component by a subclass of Identity. The subclass overrides the isLoggedIn method by a version that silently logs in the user if it is already logged in at the servlet level. It removes the Seam wrap from the request, and looks inside, to see whether the bare servlet request contains an authenticated principal. If that is the case, the user is pulled into the Seam world. Unfortunately, there is no elegant way to fetch the list of roles from the servlet request. We fell back at looping through all possible roles (hard coded), and call isUserInRole for each role.
Binding method 2: Create an Authentication Filter
The second method is inspired on Seam's support for the good-old Basic and Digest authentication. Seam provides a built in AuthenticationFilter that handles those. The filter can be configured in components.xml, with an auth-type parameter that can be either basic or digest. We dreamed about a third option: saml. But back in reality, we needed to write our own SamlAuthenticationFilter. The filter is quite basic: it looks whether the user is authenticated in the servlet request. If so, and the user is not logged in in Seam, the user is upgraded to a Seam authenticated user, by filling the identity component. This is basically the same as in method 1, with the same roles fetching problem. A difference is that we don't need to unwrap the request, because we force the filter manager to place the identityFilter (which does the wrapping) inside our filter (by using the @Filter annotation).
Installing and Running the Example Applications
If you like to see the SSO working, you can follow these steps:
- download and install JBoss AS 5.1
- download JBoss Identity for JBoss AS (we used version 1.0.0.alpha4)
- copy the jar files of the JBoss Identity distribution to the lib folder of the JBoss server
- configure an application policy named meandiLdap in conf/login-config.xml (configure it as you like, we used an apacheds LDAP server, but you don't have to use LDAP, the meandiLdap is only a name; the configuration will be used by the IDP for authenticating users)
- configure the policy Saml in conf/login-config.xml:
<application-policy name = "Saml">
code = "org.jboss.identity.federation.bindings.jboss.auth.SAML2LoginModule" />
- deploy the attached war files
The war files contain two Seam web applications (web root: sp1 and sp2) and one very simple JSP-based IDP application (web root idp). The first web application uses Seam integration method 1, the second uses method 2. Both applications have one public page (/PublicPage.seam) and one private page (/PrivatePage.seam). After deploying the war files, single sign on can be demonstrated as follows:
- open http://localhost:8080/sp1/PublicPage.seam
- follow the link to the private page; the browser is redirected to the login page of the identity provider
- supply your credentials
- private page is shown, with your username
- in the same browser session, open http://localhost:8080/sp2/PublicPage.seam
- follow the link to the private page; it will show you have been logged in silently
Browsing the Example Maven Projects
The attached zip file contains three Maven2 projects. We used Eclipse 3.4, with plugins JBoss Tools 3.0 and Maven Integration for Eclipse 0.9.8. If you use a similar setup, the Maven projects can be imported to the Eclipse workspace as Maven projects.
This is (as far as we know) the first integration effort between JBoss Identity Federation and Seam. It works, and it is not very complex. We used OpenSSO federation and fedlets in combination with Seam before, which also worked, but with much more effort. Some notes about the limitations of the presented solution, and some directions for the future:
- It would be great to have a Seam binding in JBoss Identity Federation. The envisioned binding could work in any JEE container without installing anything in the container. A developer would just include JBoss Identity Federation in her application war/ear, configure it using jboss-idfed.xml, and deploy to her preferred application server. The federation package would seamlessly integrate with Seam, without the need for specifying the same security constrains in two places (web.xml and pages.xml, like in the examples).
- The described solutions don't do anything with SAML's relayState. The relayState would be needed to redirect the user back to the secured page that he wanted to see, after she has been redirected to the IDP for authentication.
- The example doesn't use signatures. We tested it with signatures as well. This almost works (see JIRA issues JBID-132 and JBID-133).
- Currently, the only attributes that are sent from IDP to SP are the username and the roles. But the IDP might know much more than that: real names, e-mail addresses, user preferences, etcetera. SAMLv2 supports them, and it would be nice when JBoss Identity Federation could relay them to a Seam web application.
- We discussed the SP binding. An IDP binding for Seam would be very interesting as well, given the capabilities of Seam's identity management.