views:

32

answers:

1

I have a Single Sign-on solution which is working great in our test environment. It uses Cross-Sub-Domain cookies to share the authentication ticket between 2 web applications. Sign-on is done only on one of the applications and on the second site the user is authenticated by the cookie created by the first site.

The problem is that when I roll it into the production environment the single sign-on no longer works. I'm looking for any ideas on why this might be. More details below:

1) Both apps are implemented using ASP.NET MVC2

2) In our test environment both websites are sitting on a single server, with different websites in IIS and host headers used to serve the 2 web applications. In production the sites are on different servers which are geographically separated. They use the same sub-domains though.

3) Both sites have SSL setup and accessed via https; this is done in both test and production using the same self-signed wild-card certificate.

4) Users login to site1 and then the application automatically retrieves data from site2 using AJAX and JSONP.

5) if the sites are site1.example.com and site2.example.com, the following are the important parts of the web.config shared between site1 and site2:

...
<authentication mode="Forms">
  <forms name=".myapp" domain=".example.com" slidingExpiration="true" loginUrl="~/Account/LogOn" timeout="30"/>
</authentication>
...
<machineKey validationKey="KEY1..." decryptionKey="KEY2..."
  validation="SHA1" decryption="AES" />
...

NOTE: One thing I do wonder about the stuff above is, does the authentication ticket get encrypted\hashed in such a way that it can only be decrypted on the same server? That would explain my issue; but if this is the case how can I ensure that the server for both site1 and site2 can decrypt my authentication cookie? KEY1 and KEY2 are both definitely the same on both sites\servers.

6) On Site1 the Roles the user is in are also inserted into the cookie using the following:

public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl)
    {
        if (!ValidateLogOn(userName, password))
        {
            ViewData["rememberMe"] = rememberMe;
            return View(new SiteViewModel(this));
        }

        FormsAuth.SignIn(userName, rememberMe);

        // Add roles to cookie
        string[] roles = Roles.GetRolesForUser(userName);
        HttpCookie cookie = FormsAuthentication.GetAuthCookie(User.Identity.Name, rememberMe);
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
        // Store roles inside the Forms cookie.  
        FormsAuthenticationTicket newticket = new FormsAuthenticationTicket(ticket.Version, userName,
            ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, String.Join("|", roles), ticket.CookiePath);
        cookie.Value = FormsAuthentication.Encrypt(newticket);
        cookie.HttpOnly = false;
        Response.Cookies.Remove(cookie.Name);
        Response.AppendCookie(cookie);

        if (!String.IsNullOrEmpty(returnUrl))
        {
            return Redirect(returnUrl);
        }
        return RedirectToAction("Index", "Home");
    }

7) The roles are restored on Site2 using the following:

        protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        if (Context.Request.IsAuthenticated)
        {
            FormsIdentity ident = (FormsIdentity)Context.User.Identity;
            string[] arrRoles = ident.Ticket.UserData.Split(new[] { '|' });
            Context.User = new System.Security.Principal.GenericPrincipal(ident, arrRoles);
        }
    }

EDIT:

8) This is a printout of the assemblies referenced by the applications on both servers:

        mscorlib: 2.0.0.0
    System: 2.0.0.0
        System.Configuration: 2.0.0.0
            System.Xml: 2.0.0.0
    System.ComponentModel.DataAnnotations: 3.5.0.0
        System.Core: 3.5.0.0
    System.Data: 2.0.0.0
        System.EnterpriseServices: 2.0.0.0
            System.Transactions: 2.0.0.0
    System.Data.Entity: 3.5.0.0
        System.Runtime.Serialization: 3.0.0.0
            SMDiagnostics: 3.0.0.0
    System.Web: 2.0.0.0
        System.Drawing: 2.0.0.0
        System.Web.RegularExpressions: 2.0.0.0
        System.Web.Services: 2.0.0.0
    System.Web.Abstractions: 3.5.0.0
    System.Web.Extensions: 3.5.0.0
        System.Data.Linq: 3.5.0.0
            System.Xml.Linq: 3.5.0.0
        System.ServiceModel: 3.0.0.0
            System.IdentityModel: 3.0.0.0
        System.ServiceModel.Web: 3.5.0.0
    System.Web.Mvc: 1.0.0.0
        System.Web.Routing: 3.5.0.0
    xVal: 1.0.0.0
A: 

Yes. The encryption is server specific. Or being more precise relies on the machine key being the same.

  1. Try and use the same machine key in both web configs.
  2. If that dosent help. Remove encryption and try if works then

If it does not work after removing security/encryption entirely it has nothing todo with incompatible encryption. Let us know.

UPDATE This is extracted from our web.config. After removing site specifics of course. Try to specify everything explicitly, especially the path:

<forms name=".ASPNET" protection="All" loginUrl="~/Account/LogOn" timeout="2880"
        path="/" domain=".example.com"/>
ntziolis
How do I use the same machine key? In my sample web.config above the validationKey and decryptionKey are explicitly set and the same on both servers. Is there another key I need to set somewhere or are these them?
mutex
No that is the one, sry I was not looking closely enough. Did you try to remove all encryption for the authentication process?
ntziolis
Yes, removed all encryption and things work! Very confused why it doesn't work with encryption when the keys are the same. Does it add some sort of server specific info into the mix?
mutex
are both apps running on the same version of the framework? since asp.net a new ticket algorithm has been introduced
ntziolis
I Believe so. Both servers should have ASP.NET SP1 and the ASP.NET tab for the site in inetmgr has the same version (2.0) activated. I've edited the question to include a printout of all the referenced assemblies from both sites.
mutex
Try to specify everything explicitly, especially the path. I've updated my answer since the comments don't support proper code formatting
ntziolis