15 June 2010

Converting EPiServer 6 to use claims-based authentication with WIF

Claims and federated security are increasingly big news in enterprise architectures that seek to implement single sign-on across different domains and technologies.

This article describes how to modify EPiServer 6 to delegate authentication to a claims issuer as well as discussing the issues that have to be overcome to make it all hang together.

What is claims-based security?

Claims-based security is a means of de-coupling the whole process of authentication from applications and services. It’s a different way of looking at authentication – instead of users and roles being defined from a central location, you have the notion of claims that can be provided by a  number of issuers.

Claims are statements made about users – for example their name, group or privilege – and they provide a much more flexible way of describing identity than a role-based model. For example, some claims issuers support the idea of private user identities, so an application can authenticate users without having to know any personal details about them. Claims effectively de-couple roles from authorisation logic, allowing for more finely-grained permission schemas.

In claims-based security, applications are no longer responsible for authenticating users and this role is delegated to Issuers. These issuers are completely separate systems that authenticate users and make claims about them, passing the claims to applications in the form of a signed and trusted token.  This approach allows for greater flexibility in security configuration, as an application will expect to receive claims about a user, but they don’t care where these claims actually come from.

This gives rise to the notion of federated security, where users can be authenticated across different domains, organisations and technologies. In the Windows world this is supported by Microsoft’s Active Directory Federation Services, which allows Windows 2008 Server to act as a federation server and share identity with other security realms via tokens through a trust relationship.

The Windows Identity Foundation provides a programming framework that supports issuing and consuming claims in applications. It provides a model that allows ASP.NET applications to be easily converted to use claims-based authentication through HTTP.

How does this impact EPiServer?

The main impact that claims-based security has on EPiServer is that you are delegating responsibility for authentication and authorisation entirely to another system. EPiServer will not have any role in creating, managing or authorising users, nor will it have any direct access to any store of users and roles. It just accepts claims that have been created by an issuer and acts on them accordingly.

The diagram below shows [in grossly simplistic terms] the current EPiServer security model which involves EPiServer interacting directly with an authentication store via providers:

EPiServer security model without claims

The diagram below illustrates [again, pretty simplistically] how EPiServer fits in to a claims-based model, where authentication takes place in a separate system that EPiServer does not have any direct access to:

EPiServer security with claims

From a technical point of view, adapting an EPiServer site to claims-based security involves several major changes:

  • Forms authentication is not used – it is turned off in web.config.
  • Authentication takes place much earlier in the HTTP pipeline – a couple of HTTP modules are added to authenticate a user at the start of the page request cycle and re-direct them to an issuer if they are not authenticated.
  • The membership and role providers do not have any role to play in authentication – everything is handled by an external claims issuer.

Setting up EPiServer to use a token service

Setting up an EPiServer site for claims-based authentication only requires a few tweaks of the web.config file.

You will need a security token service to connect to and you can mock one up if you have installed the Windows Identity Foundation SDK – this article explains how to set up a simple security token service using Visual Studio 2008.

To make an EPiServer application claims aware, a new configuration section needs to be added to the configSections element:

<configSections>
  ...
  <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>

Next, you need to switch off forms authentication and deny access to the site:

<authentication mode="None"/>
<authorization>
  <deny users="?"/>
</authorization>

The two HTTP modules that perform the authentication should be added to the system.webServer\modules section:

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true">
    ...
    <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  </modules>
  ...
</system.webServer>

Next, you configure the Identity Foundation itself by adding a microsoft.identityFramework element into the end of your configuration section:

<microsoft.identityModel>
  <service>
    <audienceUris>
      <add value="http://EPiServer6Site/"/>
    </audienceUris>
    <federatedAuthentication>
      <wsFederation passiveRedirectEnabled="true" issuer="http://SecurityTokenService/" realm="http://EPiServer6Site/" requireHttps="false"/>
      <cookieHandler requireSsl="false"/>
    </federatedAuthentication>
    <applicationService>
      <claimTypeRequired>
        <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true"/>
        <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true"/>
      </claimTypeRequired>
    </applicationService>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <trustedIssuers>
        <add thumbprint="575BC741AAE8F063DD76615EE8152879B3C0F5EA" name="http://SecurityTokenService/"/>
      </trustedIssuers>
    </issuerNameRegistry>
  </service>
</microsoft.identityModel>

Finally, just to demonstrate that claims are really driving authentication, you can disable the membership and role providers by replacing the membership and role declarations in the system.web section with the following:

<system.web>
  ...
  <membership>
    <providers>
      <clear/>
    </providers>
  </membership>
  <roleManager enabled="false">
    <providers>
      <clear/>
    </providers>
  </roleManager>
  ...
</system.web>

This is all you need to do to get EPiServer up and running with claims. When you try to access your site you will be re-directed to your security token service website where you will submit your credentials. Once the security token service has logged you in then you’re re-directed back to EPiServer with a set of claims.

Inevitably, there are some catches…

On the surface, things work quite smoothly as claims authentication provides an identity object that implements the IPrincipal interface, so the EPiServer API seems to play nicely with claims-based credentials.

However, things get more tricky with the EPiServer UI, mainly because EPiServer expects direct access to the authentication store through the role and membership APIs, making a number of direct calls to them. Once role and membership providers have been taken out of the equation then a fair amount of the functionality in the EPiServer UI either becomes redundant or just plain fails.

The details of how you would solve these UI issues depend very much on the nature and type of claims that your are receiving from your issuer, but there are two main issues to solve.

Firstly, when assigning access right to content in EPiServer, a call is made to a role provider for a list of all the currently available roles. If you want to be able to assign access rights for content and features to roles that are returned by claims then you will need to create a custom role provider for EPiServer that supplies a list of all the valid roles that your claims issuer is likely to use.

The exact implementation of this custom role provider would depend on the number of roles, the source of roles and the frequency with which they are likely to change. Whatever the approach, there is no getting away from having to create some kind of custom role provider to serve a valid list of roles to the EPiServer UI.

The second issue is that EPiServer exposes a lot of functionality that is no longer available in a claims-aware application – e.g. creating and managing users and groups. In some cases – such as the log-out button – this functionality will just fail, while in other cases it will fail with some pretty ugly exceptions.

Again, the solution to this depends on circumstances and your users’ tolerance for a slightly flakey UI. You can resolve this issue either by educating your users (difficult, but straightforward), customising the EPiServer UI (clever, but risky) or  by creating “dummy” custom providers that return empty results for every significant method (dirty, but effective).

Summary

Although EPiServer does not provide direct support for claims-based security, the fact that its security model is pluggable and closely aligned to ASP.NET’s standard design patterns means that, with a bit of tweaking, you can get an EPiServer-based site to be claims-aware.

However, the devil is in the detail – or the EPiServer UI – and you will probably need to do some work with custom role and membership providers to deliver a solution. The exact details of these would depend on the nature of the claims that you are receiving and the authentication store that they are coming from.

Filed under Architecture, Net Framework.