Thursday, April 1, 2010

SAML, REST, smart phones and you

(or Smart devices, not so smart protocols)

I've been working on and off with a customer on a project that involves all sorts of cool buzzwords - iPhone/Android/Blackberry Apps as clients, using REST to invoke Web Services, authenticating via SAML. While I can't go into the details or reveal too much about the project there is one line of discussion that is really interesting.

First the background:
  1. A thick client, running on a smartphone, will do some sort of handshake with one web server to authenticate the user.
  2. Once the user is authenticated that server will issue the user a SAML Assertion.
  3. The client will then use the SAML assertion to authenticate to a different server and will send REST-style requests to invoke services on that server.
So something like this:



This begs the question why not just use a conventional web SSO product like Oracle Access Manager? An excellent question, the short version of which is that the Authentication Server and the REST Server are run by two different companies and don't share any infrastructure (a more common situation than you might think).

SAML actually solves a whole bunch of painful problems in this architecture - the AuthN server can sign the SAML Assertion to prove its validity and protect it from alteration and encrypt it to prevent the user from even seeing its contents. SAML also allows the AuthN server to send additional information about the user (i.e. attributes) in an extensible way - and additional attributes can be added later without needing to change any infrastructure, communication protocols or even the client.

Did you fill up your Buzzword bingo card yet?

So moving on to the problems...

Transmitting the SAML Assertion
If we were using SOAP to go from the device to the server WS-Security would have solved all of our problems. That standard spells out exactly how to attach a SAML assertion to a SOAP message so that both the client and server can understand. Unfortunately almost all "smart" devices lack a full SOAP stack. Further complicating matters is the fact that REST is a (air quote) "standard" intended to be a very lightweight way to send requests to a server and get back a structured response. Because it's intended to be so much simpler than SOAP there's very little (read no) standards around things like authentication, encryption, signing or any of the things that make SOAP a bit complicated at times.

All of which is just a long way to say that you're basically on your own figuring out how to use SAML with REST.

There are a few obvious options of how to use SAML with REST.
  1. Send the SAML assertion to the server and swap it for a cookie. Your deployment then becomes nothing more than a standard web SSO situation and your application doesn't need to worry about the SAML bits.
  2. Send the SAML assertion in every request as part of the POST data. This places the responsibility for parsing the SAML assertion into your application logic or something that can see and handle the HTTP POST data stream.
  3. Send the SAML assertion in every request as an HTTP header. This is a slight variant of #2 that is more similar to SOAP's WS-Security model where the authentication information is separated from the actual input/output parameters of the call.
I like option 1 because it pushes handling the SAML assertion out of scope, or in other words into an S.E.P. On the other hand having a thick client interacting and cooperating with a web SSO solution introduces a whole raft of other issues including properly handling things like idle and session timeouts, dealing with redirects, and a long list of others. Web SSO products were designed to interact with web browsers and their 'on the wire' behavior can be difficult to understand from an HTTP client's perspective. I'm not convinced that this is the best solution to our problem so on to options 2 and 3.

Option 2 and 3 are nearly identical - differing only in where the assertion goes in the HTTP request. That subtle difference is actually kinda a big deal and after thinking about it for a while I think I vastly prefer option 3 to option 2. Besides the logical separation of authentication and inputs I have a few other reasons, such as the fact that POST data can only generally be consumed once which is really important for my next trick.

You probably know about Servlet Filters, and if you've been reading this blog for a while you probably know about WebLogic's security framework (often called the SSPI framework). What you may not know about is how to put them together into a Servlet Authentication Filter. Basically you write a Servlet Filter that takes on responsibility for getting the authentication token and then ask WebLogic to go call the authentication provider for you. If everything works out WebLogic goes ahead and establishes a session for you. Then when your actual service wants to know who is invoking the service it can ask by calling weblogic.security.Security.getCurrentSubject().

No fuss no muss. And most importantly you don't have to commingle service logic with any code to deal with SAML, encryption keys, XML parsing or anything else unrelated to actually doing your actual work!

Session Management
One of the concerns with sending the SAML assertion along with every request is the performance impact of the XML and crypto operations. If you are invoking a simple service (hello world for example) the overhead of all of the SAML seems like it might be awfully expensive. If you had to pay that price with every request the overhead would quickly eat up your CPU cycles grinding even a reasonably fast machine to a halt under load.

Thankfully WebLogic's designers thought about this very problem.

The first and most obvious solution is to act just a little bit more like a browser. When you authenticate to Weblogic it automatically creates a session for you and sends a cookie (usually named JSESSIONID) back to your browser. If you include that cookie with subsequent requests there's no need to authenticate again. So if you smarten up the client so that it handles cookies gracefully you'll avoid WebLogic having to re-parse and validate your SAML assertion. In fact if I'm reading the relevant iPhone SDK docs (just to cite one example) correctly I think the iPhone SDK handles cookies properly for you automatically by default! Android includes Apache HttpClient which makes cookie handling almost trivial. And as for Blackberry, well it's J2ME which means you'll have to do cookie parsing by hand; which, while unfortunate, isn't the end of the world.

As long as you do the right thing with the cookies coming from WebLogic your session will be fine. If something happens to your session (e.g. the server gets rebooted, you get shunted off to another server that doesn't know about your session, your session times out, etc) the auth filter will automatically reestablish a session as long as your SAML assertion is still OK.

But that's only one part of the solution. If you disable WebLogic's issuance of cookies or you choose to not handle cookies in your thick client's code WebLogic has still got your back.

Weblogic's Identity Assertion Cache
Decrypting a chunk of XML, parsing it, and extracting some data takes some CPU cycles, but isn't all that slow. Searching an LDAP directory to find a user, then doing another search to chase down all of the group memberships on the other hand takes real, measurable clock time. Some of the time is because you're doing a search and some is because you're going over a physical wire to talk to the LDAP server and those wires (AFAIK) are still subject to the laws of physics.

The WebLogic docs describe the setting in some detail. The Javadoc for the Authentication Provider says:
The assertIdentity() method of an Identity Assertion provider is called every time identity assertion occurs, but the LoginModules may not be called if the Subject is cached. The -Dweblogic.security.identityAssertionTTL flag can be used to affect this behavior (for example, to modify the default TTL of 5 minutes or to disable the cache by setting the flag to -1).
And the command line reference fills in some more details:
When using an Identity Assertion provider (either for an X.509 certificate or some other type of token), Subjects are cached within the server. This greatly enhances performance for servlets and EJB methods with tags as well as for other places where identity assertion is used but not cached (for example, signing and encrypting XML documents). There might be some cases where this caching violates the desired semantics.
Wrapping it all up
So to summarize:
  • SAML is cool
  • smart devices are pretty cool, but they lack a SOAP stack
  • WebLogic's SSPI framework is cool
  • the WebLogic engineering team thought of darn near everything
Oh, and if you combine a Servlet Auth Filter, the SAML SSPI Identity Asserter, a teensy bit of code to handle cookies on the client side you can do some pretty clever things.

Got a comment or question? Let me know below!
----
Update: After having this up a few days I had a talk with someone out of band that in effect said "you said #1 is probably not the best way, but then you went through a whole discussion about 2/3 but wound up describing #1 and saying that it was best." So I obviously need to clarify.

What I was talking about in #1 is actually invoking a specific server. In other words login(String SamlAssertion) and have a token come back. The problem with that solution is that it's complicated and if the token isn't acceptable for some reason you need to know how to go back and get a fresh token.

In the rest of the post I describe sending the SAML Assertion on every request and doing "the right thing" when it comes to cookies. If the server sees the cookies and can find the associated session it won't bother checking the SAML assertion. If you don't have a cookie, the cookie or session is invalid or something else goes wrong then the server will go ahead and validate the SAML Assertion and establish a new one.

Hope that clears things up.

4 comments:

  1. While SAML (2.0) spec is all powerful, we have encountered significant limitation in Weblogic implementation. With Weblogic 10.3 (or 10.3.x), additional attributes are not supported, and instead one needs to rely on Groups or some other customized way to communite additional informtion between IdP and SP. Hopefully customized attributes will be supported soon in future WLS releases.

    ReplyDelete
  2. We are also developing a thin client which send the SAML assertion to the websites to prove user identity. Could you please let me know specifics of the libraries used in Android for SAML?

    ReplyDelete
  3. I'd like to second Pradeep's question and ask another one of my own.

    I'm currently stuck very much in a scenario like the one that you described, but our business partners don't want to enter our SSO credentials directly in the fat client (which is owned by the vendor). Do you have any thoughts on how to solve this issue?

    Thanks.

    ReplyDelete
  4. @Pradeep and Johnny: Android doesn't have a SOAP stack so there aren't any SAML related APIs in Android. The customer above was going to have to make all of their own code to grab the SAML assertion from the initial call. They were then going to send it using a custom, proprietary method.

    As for not entering their credentials in the client - perhaps something like OAUTH would do? That would allow the user to authorize a single device and software client without having to enter the credentials in the thick client. You'd still have to wire that into WebLogic or whatever you are using for your login system, but that's probably easier than the SAML solution above.

    Reach out to me by email if you'd like to discuss in detail - Christopher dot Johnson at Oracle dot com.

    ReplyDelete

Note: Only a member of this blog may post a comment.