2

Where’s my auth cookie gone?

When redirecting from my SSO to a client application, the .ASPXAUTH cookie is lost but only if the two sites are not on the same server.

In Fiddler, I can see the cookie being set by the SSO to the Response, in the correct cookie path for the client app. Upon redirecting however, I see that the request does not bear the cookie.

Response after logging into SSO:

Response after logging into SSO

Request back to client application: Request back to client application

Relevant sections of the Login apps web.config:

<machineKey compatibilityMode="Framework20SP2"
                decryption="AES"
                decryptionKey="<a valid RSA key>"
                validation="SHA1"
                validationKey="<a valid HMACSHA256 hash>"
                />
    <!-- "SHA1" actually implements HMACSHA256, but for one reason or another, we can't specify it explicitly. -->

    <authentication mode="Forms">
      <forms loginUrl="Index"
             cookieless="UseCookies"
             requireSSL="false"
             name=".ASPXAUTH"
             path="/path/to/SSO-Virtual-Directory/"
             slidingExpiration="true"
             timeout="20"
             enableCrossAppRedirects="true"
             protection="All"
             ticketCompatibilityMode="Framework20"
             />
      <!-- set cookie path relative to virtual path of the application in IIS. See Application -> Advanced Settings to see the virtual path.   
            Cookie Paths, Domains, and Names are all CASE SENSITIVE!!!!! 
            Be sure to check the virtual path, as it doesn't update when you rename path tokens to change case. you will have to recreate the application to update the virtualpath-->
    </authentication>

    <!--SSOConfig Providers-->
    <membership defaultProvider="SqlMembershipProvider" >
      <providers>
        <clear />
        <add name="ADMembershipProvider"
             connectionStringName="ADConnectionString"
             attributeMapUsername="sAMAccountName"
             enableSearchMethods="false"
             connectionUsername="<a valid domain username"
             connectionPassword="<a valid password>"
             type="System.Web.Security.ActiveDirectoryMembershipProvider"
             />
        <!-- do not set applicationName= .-->

        <add name="SqlMembershipProvider"
             connectionStringName="SqlConnectionString"
             applicationName="SSO"
             enablePasswordRetrieval="false"
             enablePasswordReset="true"
             requiresQuestionAndAnswer="true"
             requiresUniqueEmail="true"
             passwordFormat="Hashed"
             minRequiredNonalphanumericCharacters="0"
             type="System.Web.Security.SqlMembershipProvider"
             />
        <!-- for some messed up reason applicationName is required.-->
      </providers>
    </membership>

    <roleManager defaultProvider="SqlRoleProvider"
                 enabled="true"
                 cacheRolesInCookie="true"
                 cookieName=".ASPROLES"
                 cookieTimeout="30"
                 cookiePath="/path/to/Virtual-Directory/"
                 cookieRequireSSL="false"
                 cookieSlidingExpiration="true"
                 cookieProtection="All"
                 >
      <!--set cookie path relative to virtual path of the application in IIS. See Application -> Advanced Settings to see the virtual path. eg: /secure/sso/CentralLogin/ on Exodus.  
            Cookie Paths, Domains, and Names are all CASE SENSITIVE!!!!! 
            Be sure to check the virtual path, as it doesn't update when you rename path tokens to change case. you will have to recreate the application to update the virtualpath-->
      <providers>
        <clear />
        <add name="SqlRoleProvider"
             type="System.Web.Security.SqlRoleProvider"
             connectionStringName="SqlConnectionString"
             applicationName="SSO"
             />
        <!-- set ApplicationName-->
      </providers>
    </roleManager>

Client Web.config:

<machineKey compatibilityMode="Framework20SP2"
                decryptionKey="<The same RSA key>"
                validation="SHA1"
                validationKey="<The same HMACSHA256 hash>"
     />

    <authentication mode="Forms" >
      <forms loginUrl="~/login/Index"
             name=".ASPXAUTH"
             path="/Payment/"
             requireSSL="false"
             slidingExpiration="true"
             timeout="20"
             cookieless="UseCookies"
             enableCrossAppRedirects="true"
             protection="All"
             ticketCompatibilityMode="Framework20"
             />
    </authentication>

    <membership defaultProvider="SqlMembershipProvider" >
      <providers>
        <clear />
        <add name="ADMembershipProvider"
             connectionStringName="ADConnectionString"
             attributeMapUsername="sAMAccountName"
             enableSearchMethods="true"
             connectionUsername="<a valid domain username"
             connectionPassword="<a valid password>"
             type="System.Web.Security.ActiveDirectoryMembershipProvider"
             />

        <add name="SqlMembershipProvider"
             connectionStringName="SqlSSOConnection"
             applicationName="SSO"
             enablePasswordRetrieval="false"
             enablePasswordReset="true"
             requiresQuestionAndAnswer="true"
             requiresUniqueEmail="true"
             passwordFormat="Hashed"
             minRequiredNonalphanumericCharacters="0"
             type="System.Web.Security.SqlMembershipProvider"
             />
      </providers>
    </membership>

    <roleManager defaultProvider="SqlRoleProvider"
                 enabled="true"
                 cacheRolesInCookie="true"
                 cookieName=".ASPROLES"
                 cookieTimeout="30"
                 cookiePath="/Payment/"
                 cookieRequireSSL="false"
                 cookieSlidingExpiration="true"
                 cookieProtection="All"
                  >
      <providers>
        <clear />
        <add name="SqlRoleProvider"
             type="System.Web.Security.SqlRoleProvider"
             connectionStringName="SqlSSOConnection"
             applicationName="SSO"
             />
      </providers>
    </roleManager>

Both sites are MVC5 on .Net 4.5.2.

Does anyone have any ideas as to whats going wrong, and what I can do about it?

Community
  • 1
  • 1
Frank Thomas
  • 2,434
  • 1
  • 17
  • 28
  • And why it should be present? As I understand from your description, this is cookie for domain of SSO, so if your client application is on domain other than SSO - browser will not pass SSO cookies on request to your client application. – Evk Apr 05 '16 at 12:28
  • @Evk, Well, that is actually how forms authentication is supposed to work. I can confirm this with Fiddler using our existing SSO. Note that the Domain settings for the Forms region are unspecified, and when I look at the raw records, they have a Domain of "" which is necessary to be able to redirect to a localhost:7999 address. The cookies are however put in the correct path (verified in fiddler) for the client application. Are you suggesting any particular course of action? – Frank Thomas Apr 05 '16 at 12:34
  • Empty domain just means "use domain of the request", so it will be mapped to whatever domain you made request to. Question is: are your client and SSO share top level domain (like client is some.thing.com and SSO is thing.com) or they are on completely different domains. If the latter - your cookie from SSO won't be sent to your client. – Evk Apr 05 '16 at 12:47
  • yes and no. Because our devs need to be able to use it for internal development, the return url will often be localhost:xxxx. I have tried a domain like `"." ` but saw no change in behavior. Since our existing solution (which does not specify a forms cookie domain, and works fine between an ip address and a localhost address), I have been under the impression that `""` is the least specific domain reference, and should thus be sent on all requests. So how do I specify an unbounded domain? Either way, my attempts to use a tld have failed so far. perhaps due to IIS Express. – Frank Thomas Apr 05 '16 at 12:52
  • There is no such thing as undounded cookie. Every cookie is mapped to some domain (you also cannot set cookie for top level domain like .com). If you provide domain explicitly - it can also be used by subdomains of this domain, but never by other domains. Naive SSO with cookies will not work if you want to use it for completely different domains, you have to use some tricks. See this for example: http://stackoverflow.com/questions/2056686/asp-net-forms-authentication-and-multiple-domains – Evk Apr 05 '16 at 12:56
  • @Frank this is simply not how http works, I'm afraid. You'll need to follow Evk's advice and redesign your sso process. – Richard Szalay Apr 05 '16 at 13:25

1 Answers1

4

So as we seems to found out in comments, the problem is SSO and client reside on different domains\ips and so cookie set for SSO will not be passed to client by browser. There are different ways to solve this, but they require change of how your general SSO process works.

As I understand you only have problem with that in development environment, not on production. If so, suppose your SSO is on 10.0.0.1 and your client is on 127.0.0.1. Then map client.yoursite.local domain (in your corporate DNS or just in /etc/hosts file) to 127.0.0.1 and yoursite.local to 10.0.0.1, and use domain names instead of raw ip addresses. Then in SSO set cookie with domain of ".yoursite.local". This then should be delivered correctly to your client application, and will not require significant changes to how your SSO process works.

Evk
  • 98,527
  • 8
  • 141
  • 191
  • That is a very sensible solution. I will expirement with it, and see where that leads me. The other alternative I've been looking into involves passing the cookie value on the querystring, which is looking promising, but is a pretty gross solution, and to be avoided if possible. – Frank Thomas Apr 05 '16 at 15:14
  • @FrankThomas - you could manually add it to the http header as well. – Igor Apr 05 '16 at 18:56
  • Adding it to the query string is *extremely bad*. That cookie is what authorizes a user, so it should be protected at all times. HTTPS will protect the cookie normally because it's sent as a request header which is encrypted. The only thing HTTPS *can't* encrypt is the URL itself, which your solution would include the cookie value in. Anyone sniffing the network could take the value from the querystring and assume that user account. – Chris Pratt Apr 08 '16 at 15:57
  • I have to give you the props. I was not able to make the hosts hack work, because of the way visual studio integrates with IIS8Express (won't let you use a domain name other than localhost), but I now completely understand the problem, tell the operational difference between the production and development environments, and craft a workable plan. Thanks! – Frank Thomas Apr 11 '16 at 12:18