SAML Authentication for Web Applications
Posted on July 16, 2010 by Tommy McGuireSince last fall, I have written a number of posts about various ways of authenticating web application users, such as Active Directory and SPNEGO/Kerberos. Hopefully, this and any immediate follow-ons will be the last of them for a while. And I really mean that, because I am tired of the churn. (Although the Active Directory stuff is actually being used at the moment. Yay!)
This post is intended to document the basic process of authenticating web application users using SAML, the Security Assertion Markup Language, and the related protocols, bindings, and what-not.
This whole process started because my organization is using Sun Access Manager (SAM), a.k.a. OpenSSO, a.k.a. OpenAM. In our case, OpenSSO had a Policy Agent, in the form of an Apache httpd module, which squatted in front of the web application server and validated requests before the application saw them. It works by redirecting the user's browser to a OpenAM server which performs the authentication and redirects the browser back to the application in the case of success. By "works", I mean only for GET and POST requests, periodically mangling the requests it does pass, and occasionally locking up httpd in the face of BlazeDS traffic. Couple that with some serious teething problems with the SAM servers and the general performance of something like 11 redirects and automated POSTs, and some extra work to investigate alternatives sounds good. In any case, the alternatives to Sun Access Manager/OpenSSO/OpenAM have been rejected for various non-technical reasons.
The current iteration involves integrating with SAM via SAML2, the Security Assertion Markup Language, version 2. The integration is performed by a Java Servlet filter and some other components written by yours truly, rather than an off-the-shelf policy agent written by crazy people. [Ed: Other crazy people.]
There are, roughly speaking, five parts to my SAML2 authentication system. One is the user's web browser, identified as a "User Agent" (UA) below. Another is the SAM authentication server or servers, collectively identified by the SAML term Identity Provider (IdP) below. Because the actual authentication process is completely undetermined, the actual make-up of the IdP is somewhat fuzzy.
The remaining three parts make up what SAML would call a Service Provider (SP), I believe. The most important part is the web application itself, App. For our case, the App is a traditional Java web application. A Java servlet Filter sits in front of part or all of the App's URL space, blocking unauthenticated access and triggering the authentication process. Finally, there is the SAML2 Assertion Consumer Service (ACS). The response to the SAML2 authentication request, which is sent to the ACS, is one or more assertions about the user being authenticated which provide the SP with the necessary information.
In order to properly understand how the SAML2 authentication takes place, I have done something I rarely do: drawn an honest-to-gosh diagram. Although it may suspiciously resemble a UML sequence diagram, I have no idea if it is valid UML or not. The UML sequence diagram courtesy of UMLet, the best UML tool I have found.
- The web browser sends a request to the application, which is intercepted by the SAML2 filter. This request is stored for future use by the filter, while
- The filter responds with a redirect status code, redirecting the browser to the SAML2 Identity Provider (IdP) and including a SAML2 Authentication Request, cryptographically signed, as a query parameter.
- The user, browser, and IdP collaborate (noisily sometimes) to authenticate the user.
- The ACS decodes the Authentication Reply and notes that the user has been authenticated. It replies to the browser with a redirect back to the application's original URL.
- The filter intercepts the redirection, sees that the user has been authenticated, reactivates the original request, and forwards that request to the application proper.
- The application replies to the request, and
- Continues to respond to future user requests until the user logs out or the authentication expires.
The SAML scheme allows Single-Sign-On by maintaining session information between the browser and the IdP; if the user has already authenticated or is capable of authenticating automatically via SPNEGO, for example then step 3 reduces down to the request to the IdP identifying the browser. In fact, because we are capable of sharing information between applications, any application using the SAML2 endpoint filter will use the same authentication information, removing the need to go through any kind of authentication step when moving between our supported applications.
While it is not currently used in our case, the SAML approach also supports privacy since the user only exchanges identifying information with the IdP and the SP and application trust the judgment of the IdP. The authentication response contains a user identifier, an effectively random token that is associated with the user either for this session or permanently, for multiple sessions. Optionally, the response also contains assertions about the user, including in our case the user's actual identifying information, such as a user id. If someone desired an application that should not know anything about a user other than that they were authentic, the SAML response could be configured to only contain the identifier, not the assertions.
SAML is a three-humped camel with a tail like a '57 Chevy. [Ed: Really, the tail looks like a '57 Chevy.]
Seriously, the Security Assertion Markup Language is developed by OASIS. At its core is an XML specification for sharing security related assertions. SAML is, however, much bigger than its core. It includes what they call "protocols", defining what SAML-formatted messages should be sent under specific circumstances such as authentication request, logouts, assertion queries, etc. The SAML family also includes what they call "bindings", which provide the details about how protocol messages should be presented in standard message formats such as HTTP Redirect (used by the authentication request above) and HTTP POST (used by the authentication reply above), SOAP, etc. Also, there are "profiles"; "metadata" to be exchanged; conformance, security, and privacy considerations; and a three-legged water buffalo named Elmont.
For an example that puts the whole scheme in perspective, consider the Authentication Request (AuthnRequest, not to be confused with an Authorization Request, or AuthrRequest) made in step 2 above. The metadata I have suggests that that should be done using the HTTP Redirect binding, which means the request is made by returning a HTTP redirect response code with the arguments passed to the IdP in the Location header as query parameters. The AuthnRequest is an XML document identifying the requester, specifying the desired level of assurance, and so forth.
Document formatting standards bodies may not provide the best network protocols, that is all I want to say.
By the way, you may be wondering why I carefully stored the original request in step 1 and restarted it in step 6. Instead, could I not just direct the user to a nice "Welcome" page? Consider what happens after a user has logged in and begun using the application: at some point, the user's identity information for the application will expire and the next request the user makes will appear to be unauthenticated. This expiration could happen after thirty minutes, or it could happen after five. (Hey, it's cheaper than having security escort ex-employees out of the building, right?) That request could be anything; an AMF/BlazeDS request, posting a form that will end-of-life a well-traveled, expensive piece of equipment, even a request for a safe welcome page, or whatever. Now, it is likely that storing the request will leave the application in some bizarro state anyway, but it seems like the best chance of handling the weirdness gracefully.
 Which may not be saying much.