Following on from Ben Morris' blog about Converting EPiServer 6 to use claims-based authentication with WIF I was intrigued to see if it was possible to create a role and membership provider that allowed us to use WIF in EPiServer with little or no modification apart from configuration changes.
I set out with the following list of requirements for my EPiServer WIF solution:
- Plug in to EPiServer with no code changes, only config changes
- All edit/admin functionality to work the same as the Windows providers (i.e. read only access)
- Scheduled jobs and workflows to execute with no modification (i.e. EPiServer.Security.PrincipleInfo.CreatePrinciple() should continue to work)
I made the following assumptions:
- The WIF providers will not be multiplexed
- There will be claims that can be translated into roles for use in EPiServer
- Users will only have a single identity (IClaimsPrincipal can support multiple IClaimsIdentity items)
Creating the WIF role and membership providers
The biggest drawback of WIF when it comes to EPiServer is the fact that EPiServer expects the role and membership providers to have access to role and membership stores. However this isn't possible under WIF as the user comes to EPiServer with a set of claims. So it was obvious that we needed a set of membership and role providers that provide a local cache. This will allow us to store membership and role data in a local cache when a user is first authenticated. This will allow the standard edit/admin functions to work correctly and also allow scheduled jobs and workflows to execute with no modification. The local cache can be provided by inheriting the standard SQL membership and role providers and modifying as follows:
It can be seen that I used the "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" claim for the roles but this can be changed to map to any claim that comes back from the security token service (STS).
Hooking into the WIF events
As we are caching the users and roles locally we need to know when a user has signed in to synchronise the cache. This is achieved by inheriting from the standard WSFederationAuthenticationModule overriding the OnSignedIn method and synchronising the providers. This allows us to pick up the claims from the STS and save them out to the local membership and role cache:
Now we need to replace the standard WIF WSFederationAuthenticationModule with the custom module in web.config as follows:
We want to mark the providers as only having read only access to their store so this is achieved with a EPiServer 6 initialisation module. This used the EPiServer ProviderCapabilities collection which can be used to mark the WIF providers as being read only:
Seeing it action
At this point what we have is a set of role and membership providers that allows EPiServer to see WIF claims through read-only role and membership providers:
However site editors and administrators can still use the user and group functions as they would normally:
I put together a super simple scheduled job to show the new providers in action. It simply executes a single line of code as shown below:
Obviously apart from the initialisation module and scheduled job this isn't an EPiServer specific implementation so could be used on any ASP.net application.
Signing in and signing out
By implementing WIF we are federating our sign in and sign out capabilities to a 3rd party STS. So to log in we need to redirect to actually sign in and come back to the current page. This is achieved as follows:
Similarly there is a federated sign out too:
Testing with Security Token Services
I tested this approach using ADFS 2.0 connecting to Active Directory where I mapped the name claim to the AD username and AD groups to the roles claim. I also tried with a custom STS as described in Ben Morris' post here: http://www.ben-morris.com/creating-a-security-token-service-website-using-wif-in-visual-studio
This is simply a proof of concept and is not yet suitable for a production environment. But I would be very interested to hear from ideas on how to take this forward. I’d also like to hear how EPiServer plan to integrate WIF into EPiServer.
I think WIF integration could really drive EPiServer up the value chain and mark the CMS as even more enterprise focussed.
One final note. Since this part of this solution uses an EPiServer initialisation module, its worth remembering Magnus Stråle's note about debugging initialisation modules: http://world.episerver.com/Blogs/Magnus-Strale/Dates/2010/8/Debugging-initialization-modules/
Download EPiServerWIF.zip for the source code