Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Internet Explorer and Cookie Domains


:P
On this page:

I've been bitten by some nasty issues today in regards to using a domain cookie as part of my FormsAuthentication operations. In the app I'm currently working on we need to have single sign-on that spans multiple sub-domains (www.domain.com, store.domain.com, mail.domain.com etc.). That's what a domain cookie is meant for - when you set the cookie with a Domain value of the base domain the cookie stays valid for all sub-domains.

I've been testing the app for quite a while and everything is working great. Finally I get around to checking the app with Internet Explorer and I start discovering some problems - specifically on my local machine using localhost.

It appears that Internet Explorer (all versions) doesn't allow you to specify a domain of localhost, a local IP address or machine name. When you do, Internet Explorer simply ignores the cookie. In my last post I talked about some generic code I created to basically parse out the base domain from the current URL so a domain cookie would automatically used using this code:

private void IssueAuthTicket(UserState userState, bool rememberMe)
{
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, userState.UserId,
                                                         DateTime.Now, DateTime.Now.AddDays(10),
                                                         rememberMe, userState.ToString());
    
    string ticketString = FormsAuthentication.Encrypt(ticket);
    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticketString);
    cookie.HttpOnly = true;
    
    if (rememberMe)
        cookie.Expires = DateTime.Now.AddDays(10);

    var domain = Request.Url.GetBaseDomain();
    if (domain != Request.Url.DnsSafeHost)
        cookie.Domain = domain;
HttpContext.Response.Cookies.Add(cookie); }

This code works fine on all browsers but Internet Explorer both locally and on full domains. And it also works fine for Internet Explorer with actual 'real' domains. However, this code fails silently for IE when the domain is localhost or any other local address. In that case Internet Explorer simply refuses to accept the cookie and fails to log in.

Argh! The end result is that the solution above trying to automatically parse the base domain won't work as local addresses end up failing.

Configuration Setting

Given this screwed up state of affairs, the best solution to handle this is a configuration setting. Forms Authentication actually has a domain key that can be set for FormsAuthentication so that's natural choice for the storing the domain name:

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login"
             name="gnc"
             domain="mydomain.com"
             slidingExpiration="true"
             timeout="30"
             xdt:Transform="Replace"/>
    </authentication>

Although I'm not actually letting FormsAuth set my cookie directly I can still access the domain name from the static FormsAuthentication.CookieDomain property, by changing the domain assignment code to:

if (!string.IsNullOrEmpty(FormsAuthentication.CookieDomain))
    cookie.Domain = FormsAuthentication.CookieDomain;

The key is to only set the domain when actually running on a full authority, and leaving the domain key blank on the local machine to avoid the local address debacle.

Note if you want to see this fail with IE, set the domain to domain="localhost" and watch in Fiddler what happens.

Logging Out

When specifying a domain key for a login it's also vitally important that that same domain key is used when logging out. Forms Authentication will do this automatically for you when the domain is set and you use FormsAuthentication.SignOut().

If you use an explicit Cookie to manage your logins or other persistant value, make sure that when you log out you also specify the domain. IOW, the expiring cookie you set for a 'logout' should match the same settings - name, path, domain - as the cookie you used to set the value.

HttpCookie cookie = new HttpCookie("gne", "");
cookie.Expires = DateTime.Now.AddDays(-5);

// make sure we use the same logic to release cookie
var domain = Request.Url.GetBaseDomain();
if (domain != Request.Url.DnsSafeHost)
    cookie.Domain = domain;

HttpContext.Response.Cookies.Add(cookie);

I managed to get my code to do what I needed it to, but man I'm getting so sick and tired of fixing IE only bugs. I spent most of the day today fixing a number of small IE layout bugs along with this issue which took a bit of time to trace down.

Posted in ASP.NET  

The Voices of Reason


 

Steven Berkovitz
April 25, 2012

# re: Internet Explorer and Cookie Domains

If you are doing this, then you likely also want the ASP.NET Session ID cookie to span these domains too. This can be done via the HttpApplication.PreRequestHandlerExecute event. The following code block works well for me.

if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState) {
                    // Ensure ASP.NET Session Cookies are accessible throughout the subdomains.
                    if (Request.Cookies["ASP.NET_SessionId"] != null && Session != null && Session.SessionID != null) {
                        Response.Cookies["ASP.NET_SessionId"].Value = Session.SessionID;
 
                        string currentHost = HttpContext.Current.Request.Url.Host;
                        string[] hostParts = currentHost.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
 
                        if (hostParts.Length > 1) {
                            // Just use the domain and TLD part
                            Response.Cookies["ASP.NET_SessionId"].Domain = string.Concat(".", hostParts[hostParts.Length - 2], ".", hostParts[hostParts.Length - 1]);
                        }
                    }
                }

Brock
April 25, 2012

# re: Internet Explorer and Cookie Domains

It's been a while since I've tested, but perhaps IE is being very strict when it comes to the cookie RFC -- when a domain is explicitly specified it must start with a "." (from https://tools.ietf.org/html/rfc2109). Maybe you're doing this in your real code, but the sample you have above didn't specify the domain.

Jared
April 25, 2012

# re: Internet Explorer and Cookie Domains

Have you tried adding an entry to the hosts file for localhost.domain.com set to 127.0.0.1 and then using localhost.domain.com in IE when doing your local development?

Matt
April 25, 2012

# re: Internet Explorer and Cookie Domains

Thanks for sharing, this is good to know. Would it work if you omit the domain property based on a check to HttpRequest.IsLocal? That way you could ditch the configuration step.

Related, I've done some work recently setting the path property of cookies. I ran into cross browser issues where some browsers treat the path as being case sensitive. This means if you set the path attribute to "/MyApp" it will not be sent by some browsers for requests for "/myapp"

Rick Strahl
April 25, 2012

# re: Internet Explorer and Cookie Domains

@Matt - yes I've found lots of problems with using paths to the point where I've completely given up using paths in cookies - too damn unpredictable. Instead I try to make sure each application has unique cookies including those used for Forms Auth configured in web.config for the application to avoid the ASPNETSESSION cookie for all apps.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024