Tuesday, December 13, 2016

Sitecore SSO Part 2

This is the second article of the SSO blog posts. You can find the first one here

Sharing Authentication cookie across 2 Sitecore instances:

As I mentioned in my first post, we now want to share the authentication cookie across 2 different sitecore instances (6.5 and 8.1)
Sitecore 8.1 and 6.5 behave differently when dealing with authentication. 
Both use 2 cookies for authentication 
  • .ASPXAUTH (name specified in web.config) 
  • sitecore_userticket
Every version deals with authentication cookie in a different way
  • Sitecore 6.5: When user logs in, both cookies are created. It only uses the .ASPXAUTH cookie for authentication. If you delete or modify the content of sitecore_userticket cookie using a browser plugin, it will not be regenerated. Sitecore is not affected if you modify the content of sitecore_userticket  cookie
  • Sitecore 8.1: When user logs in, both cookies are created. It only uses sitecore_userticket cookie for authentication. If it is missing or modified, then you are considered logged out even if you have the other cookie. sitecore_userticket content is different than Sitecore 6.5 cookie. So sharing it won't make logged in users in Sitecore 6.5 logged in Sitecore 8.1.  .ASPXAUTH cookie can be deleted safely and it will be recreated with every request. You will get an ASP.Net Exception though, if you modify the content of the .ASPXAUTH cookie.
To summarize
  • sitecore_userticket: Different across versions. Important only for Sitecore 8.1 
  • .ASPXAUTH: Same across versions. Important only for Sitecore 6.5
The solution I implmented to solve this was:
  1. Share .ASPXAUTH cookie across instances as both have the same top domain
  2. Recreate the sitecore_userticket in Sitecore 8.1 if .ASPXAuth cookie exists.

Sharing .ASPXAUTH cookie:

You will have to share the same machine key specified in the web.config in BOTH sites:
<machineKey validationKey="xxxxxxxxxxxxxxx"  decryptionKey="xxxxxxxxxx" validation="SHA1" decryption="AES" />
Make the Authentication cookie domain the top domain used by BOTH instances 

<authentication mode="None">
<forms name=".ASPXAUTH" cookieless="UseCookies" domain="[Your top domain here]"/>
</authentication>

Recreating sitecore_userticket  Cookie:

I modified a solution is specified here. The solution specified in the link is good to share the ticket between 2 instances of the same version. In this case sharing the cookie is enough between the domains. But as the content of the cookie is not the same across the old and new versions of sitecore, we have to regenerate it again by adding the code below in the Global.asax file in the NEW sitecore instance. (This was a question I asked on stack exchange here)

protected void Application_EndRequest(object sender, EventArgs e)
{
    var authCookie = HttpContext.Current.Response.Cookies["sitecore_userticket"];

    if (!Request.IsAuthenticated || authCookie == null)
    {
        // when checking response cookies, cookie is created if not exists, so delete now
        HttpContext.Current.Response.Cookies.Remove("sitecore_userticket");
        return;
    }

    //we don't need to make it cross domain as it will be different for every instance 
    //due to version differences.
    //create the ticket cookie. Every Sitecore instance will generate it the way it expects.
    authCookie.Value = TicketManager.CreateTicket(HttpContext.Current.User.Identity.Name, string.Empty);
}

Conclusion:

Now you will have the sign on shared across the 2 versions. This solution may still have some minor issues but all the major challenges are fixed.

Sitecore SSO Part 1

This blog post is about SSO across 2 different instances of Sitecore while sharing the same top domain i.e. cookies can be shared across the 2 instances and both instances can access the same DB

I was recently tasked with implementing SSO across two different sitecore sites that are sharing the same top domain. Every instance has different version (8.1 and 6.5) and every instance has its own (core/master/web) databases.
To implement SSO you will have to:
  1. Share authentication/authorization/profiles info
  2. Share authentication cookie across sites
I am splitting them to 2 posts for the sake of clarity

Sharing Authentication/Authorization/Profile info:

Sharing Databases:

Sitecore uses the normal ASP.Net tables schema and builds upon the membership/roles/profiles provider. These providers use core database by default as specified in the Web.Config. To be able to share this existing info, we will need to make changes to the configuration of the new site. So first we need to add a connection in App_Config\ConnectionStrings.config file in the new instance to point to the existing core database of the old site. Let's call this connection oldcore
<add name="oldcore" connectionString="[old site core database connection string]" />
Then we go to the web.config in the new site and point the profile/membership/roles tags to the new oldcore db. Changes are highlighted in yellow (I removed irrelevant configuration for clarity)

<membership defaultProvider="sitecore" hashAlgorithmType="SHA1">
      <providers>
        <clear />
      .....
        <add name="sql" type="System.Web.Security.SqlMembershipProvider" connectionStringName="oldcore".... />
     .........
      </providers>
    </membership>
    <roleManager defaultProvider="sitecore" enabled="true">
      <providers>
        <clear />
        ........
        <add name="sql" type="System.Web.Security.SqlRoleProvider" connectionStringName="oldcore" applicationName="sitecore" />
        .......
      </providers>
    </roleManager>
   <profile defaultProvider="sql" enabled="true" inherits="Sitecore.Security.UserProfile, Sitecore.Kernel">
      <providers>
        <clear />
        <add name="sql" type="System.Web.Profile.SqlProfileProvider" connectionStringName="oldcore" applicationName="sitecore" />
        .....
      </providers>
      .........
    </profile>

This way we only shared the needed info not the whole core database which wouldn't have worked due to version differences

Sharing Domains and User Profiles:

Domains and User profiles in the add user dialog (sitecore 6.5). Client specific info obscured

Domains info are saved in the file App_config\security\domains.config. So this will need to be copied over to the new instance. 
Custom user profiles templates are in the core db in the folder: /sitecore/templates/System/Security
The user profile items themselves are in the core db in the folder /sitecore/system/Settings/Security/Profiles
The easiest way to copy the templates and the items over to the new instance is to create a sitecore package and install them in the new instance.

Profiles Versions incompatibility: 

Due different versions Profiles modified in Sitecore 8.1 cannot be loaded in Sitecore 6.5. While profiles created in Sitecore 6.5 are loaded in Sitecore 8.1
The solution for this was hacky. I had to decompile the new Sitecore.Security.UserProfile from Sitecore 8.1 Sitecore.Kernel.dll and then create in a project in the old sitecore version and then inherit from UserPorfile class in the Sitecore 6.5 Sitecore.Kernel.dll. Then fix all compilation error and remove all the properties the already exist in the old class

namespace UserProfile
{
public class UserProfile : Sitecore.Security.UserProfile
{
         .......
        }
}

and then you will need to modify the web.config in the old Sitecore to be 

<profile defaultProvider="sql" enabled="true" inherits="UserProfile.UserProfile, UserProfile">
I removed some redundant code but didn't go and spend much time to remove as much as possible of redundant code (You can find it here). You can optimize this part. But currently this code allowed Sitecore 6.5 to load profiles modified in Sitecore 8.1
Also there is another problem of caching in each instance where the user profile will load from cache so you will need to reload the profile every time you deal with it.

Conclusion:

Now both sitecore instances share the same login and now we all we need is to share the authentication cookie.