Wednesday, July 1, 2009

OpenId SSO for WebLogic Server

With the launch of 11gR, there is a new way to share code samples within the Oracle community. Check out samplecode.oracle.com

To that end, I thought I'd make my community contribution in the form of an
IdentityAsserter that provides SSO to WLS using OpenId

There were a couple of reasons to do this, but the main one was to build a reasonable example of the ServletAuthenticationFilter. WLS uses it internally for the SPNEGO support, and SAML SSO support, but I'd never really had a need from a customer to use it until recently. The need here was OpenId. On the OpenId side, I used the OpenId4Java. If people look at the sample, I'm sure there is a lot more that can be done, and I've only done very cursory testing with the Personal Information Portal at Verisign Labs. Again, this is not exactly my focus, so apologies in advance for any issues there.

So, what is the ServletAuthenticationFilter? Well its really just a special type of IdentityAsserter. It's an IdentityAsserter that adds a Filter to every web application inside of WLS. The filter only gets called when the user is accessing a protected resource, but before regular authentication kicks-in. The key is that this happens before authentication. This allows the filter implementation to go do something, like challenge the user for a different type of credentials other than what WLS supports natively. Also, the ServletAuthenticationFilter has the notion of multi-part challenges, like those that are required to do SPNEGO or OpenId. There is an initial step, and then multiple continue steps, until the challenge is completed. There are a bunch of different interfaces, and it can be a little confusing, but here's my best explanation:

The Filter logic:
1. Is there an existing challenge context - normally stored in the HttpSession.
a. If there is, call continueChallengeIdentity
b. If there isn't, call assertChallengeIdentity
2. Check if the challengeContext.hasChallengeContextCompleted
a. If it has, get the Subject and call ServletAuthentication.runAs(subject,request) - this pushes the subject onto the WLS Stack
b. If it hasn't, get the ChallengeToken. Process the token, and return it to the user.

The Identity Asserter Logic:
1. Build the Basic Identity Asserter First - this is just the plain old IA that sets up a CallbackHandler. This is based on the token type that you're going to extract. In my example, I just extended the SimpleSampleIdentityAsserter, so really my tokens are just usernames.
2. Implementing the ServletAuthenticationFilter interface
a. All that this really does is just instantiate the filter...very simple.
3. Implementinf the ChallengeIdentityAsserterV2 interface
a. The assertChallengeIdentity is going to get called when the filter calls the AppChallengeContext.assertChallengeIdentity, so this is the intial call. In this method, you'll need to instantiate your implementation of the ProviderChallengeContext, and return it.
b. The continueChallengeIdentity method gets called when the filter calls AppChallengeContext.continueChallengeIdentity. This time you get passed the ProviderChallengeContext that you created in the assertChallengeContext.
c. When you finally have a valid user, you need to set the CallbackHandler on the ProviderChallengeContext. This is simple, since you're already inside of an IdentityAsserter. Basically, just call assertIdentity, get the CallbackHandler, mark the ProviderChallengeContext has completed and you're done.

So, by looking at the Filter and IdentityAsserter implementations, we can start to understand what the purposes of some of the other objects in the model. The ProviderChallengeContext is used to hold the start of the challenge. Its typically stored in the user's session, so you can just use instance variables inside of it to hold the state. One more thing about the sessions. The filters are instatiated just like other regular filters, which means that they are scoped to applications - configured globally, but all running inside of the application. The sessions that you get from the HttpServletRequest are tied to that application. If you want to pass information between apps, you'll need to do it on the URL.

The other implication, is that the methods on the IdentityAsserter take a ContextHandler, but the methods of the AppChallengeContext take an AppContext. Basically, under the covers the AppContext is wrapped/converted into a ContextHandler. This probably means that you'll need to build you own implementation of the AppContext to hold your data. This included the HttpServletRequest and HttpServletResponse objects.

That's basically it. The rest of the information is readily available in the product documentation. I encourage people to download and try the sample. As always, comments welcome.

1 comment:

  1. Is it only for Weblogic 11g?
    Will this work with Weblogic 10gR3?

    ReplyDelete

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