UPDATE : There is a new version of the module here: https://marketplace.sitecore.net/en/Modules/ADFSAuthenticator.aspx
Over the past few months I’ve done some work integrating Sitecore with multiple Federated Authentication systems like Ping Identity, ADFS and some home grown ones. The way Federated Authentication works is instead of logging directly into an application the application sends the user to another system for authentication. Once that system authenticates the user an encrypted token, typically SAML, is passed back to the requesting application containing credentials and other information, known as claims.
Getting everything working with the Windows Identity Foundation proved to be more challenging than some of the other API’s I worked with. I’d like to thank Owain Jones, Kern Herskind Nightingale and Ivan Sharamok for their help and suggestions that made this all work.
Here is the source and update file for this solution. There is also this sample web.config.
Step 1: Setting up your development environment for claims based authentication.
This tutorial is already written so I’m just going to point to that. I suggest you set up a basic website and get claims Authentication working there before you go and try integrating it with Sitecore.
Step 2: Add Roles to Sitecore
Login To Sitecore and create the following roles :
Assign Sitecore Author to the Sitecore Client Authoring Role so they can login to the system.
Step 3: Modify the mock STS to send the roles
After you have completed that tutorial modify the STS project and change the code in CustomSecurityTokenService.cs that writes out the claims to include two roles that exist in your Sitecore system.
protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
if (null == principal)
throw new ArgumentNullException("principal");
ClaimsIdentity outputIdentity = new ClaimsIdentity();
// Issue custom claims.
// TODO: Change the claims below to issue custom claims required by your application.
// Update the application's configuration file too to reflect new claims requirement.
outputIdentity.Claims.Add(new Claim(System.IdentityModel.Claims.ClaimTypes.Name, principal.Identity.Name));
outputIdentity.Claims.Add(new Claim(ClaimTypes.Role, "Registered Users"));
outputIdentity.Claims.Add(new Claim(ClaimTypes.Role, "Author"));
Step 4: Install the FedAuthenticator update package.
Login to Sitecore as an admin
Browse to /sitecore/admin/updateinstallationwizard.aspx
Install the update file found here.
Step 5 : Using Visual Studio or FedUtil.exe update the web.config of your sitecore solution to point to your STS like you did in the tutorial found under Step 1.
see : http://www.codeproject.com/Articles/278940/Claim-based-Authentication-and-WIF-Part-2
Step 6: Modify the web.config file.
Under the Modules Section move the Authentication Modules so they fire after Nexus and replace the Session Module with the one in the FedAuthenticator Module.
<add type=”Sitecore.Nexus.Web.HttpModule,Sitecore.Nexus” name=”SitecoreHttpModule” />
<addname=“WSFederationAuthenticationModule” type=”Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=22.214.171.124, Culture=neutral, PublicKeyToken=31bf3856ad364e35″ preCondition=”managedHandler” />
<addname=“SessionAuthenticationModule” type=”FedAuthenticator.Authentication.WSSessionAuthenticationModule, FedAuthenticator”/>
<add type=”Sitecore.Resources.Media.UploadWatcher, Sitecore.Kernel” name=”SitecoreUploadWatcher” />
Change the Location information inside of the web.config file so it looks like the following:
<location path=”sitecore modules/Shell/FedAuthenticator/sso.aspx”>
<deny users=”?” />
<!– Continue to run Sitecore without script validations –>
If you now try to hit sitecore modules/Shell/FedAuthenticator/sso.aspx the user should be logged into Sitecore with the Author Role.
Step 7: Create a secured page in Sitecore.
Under the Home Item create a new Sample Item and name it ‘secured’.
Set the permissions up to break inheritance for Extranet/Everyone then grant read access and enable enheritance for the Extranet/Register Users role.
Step 8 : Test authentication to the front end of the website.
Log out of Sitecore:
Clear your cookies to remove the pre-existing token.
Browse to http://yourwebsite/secured
You should be redirected to the STS token issuer. Once you log in you will be brought back to your website and should see the secured item.
It may be helpful to add this code to your sample layout.aspx page so you can see the users information.
<div>Is Virtual? <%=Sitecore.Context.User.RuntimeSettings.IsVirtual %></div>
foreach (var role in Sitecore.Context.User.Roles)
Ideally this solution would have had two processors and nothing else.
The TokenRequestor looks to see if access is denied to an item and sends the user to the STS with a request for a token if the user is not already authenticated.
The UserResolver looks for a token in a cookie, if it exists and the user is not authenticated we log the user in a virtual user.
That’s all I expected to need when I first wrote this but I was having trouble persisting user information. This led us to the Sitecore support portal and we found we needed a custom authentication provider and we had to overwrite the WSSessionModule to get this to work.
The root of the problem is three fold:
- Both Sitecore and the Windows Identity Foundation are fighting over the threads user identity located at HttpContext.Current.Request.User.
- The Windows Identity Foundation does not allow you to just request and parse a token just using the API. It forces you to use the http modules. If I could do this without the modules there would be a lot less code. This is the approach I took when integrating Ping Identity and it was much easier but my Ping Identity code only works with Ping Identity and this solution should work with a lot of other identity providers, including Ping Identity.
- The Sitecore Nexus API is obfuscated and I had trouble figuring out where/when the Identity was being set.
So, now here’s all the ‘other’ code that was added to get the two systems to play nice.
WSSessionAuthenticationModule was added to prevent some of the Session overriding. The binding to PostAuthenticateRequest was removed from initialize module and we override the Authenticate Request method so it exits the method if we already have a Sitecore.Context.User.
The FederatedAuthenticationProvider was also added to get the role and user information to persist. The Forms Authentication Provider uses a cookie for saving the value but this cookie wasn’t persisting when we logged in using SSO so we read the Session Token instead inside of the GetCurrentUser method of the Authentication Helper.
The login method was also changed to not write out a forms cookie.
The sso.aspx page is being used to log in to Sitecore. The user is assigned to the roles based on the Sitecore Domain. That is why the page currently lives under the sitecore modules folder.
Things that could use improvement
- Currently the user is logged in on every request when a token exists. I’d like to be able to just parse the token the first time it’s sent from the STS and log the user in once but that didn’t work out.
- Single Sign Out is not yet implemented.
- This solution is currently only supporting virtual users.