I've noticed that there are some real inconsistencies between browsers in terms of cookies.
This is going to be rather long so bear with me.
Note: I've setup a domain in my host file called "testdomain.com", this bug WONT work when using "localhost".
Note2: I am curious to know how this works on Apache/PHP if when you retrieve a cookie by name if it gives a collection of cookies back.
Wikipedia
Wikipedia states that: http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Path
Domain and Path
The cookie domain and path define the scope of the
cookie—they tell the browser that cookies should only be sent back to
the server for the given domain and path. If not specified, they
default to the domain and path of the object that was requested.
So if we push down:
Response.Cookies.Add(new HttpCookie("Banana", "2")
{
});
We should get a cookie with the domain used being the domain from the requested object, in this case it should be "testdomain.com".
W3
W3 states in the specification for cookies: http://www.w3.org/Protocols/rfc2109/rfc2109
Domain=domain
Optional. The Domain attribute specifies the domain for which the
cookie is valid. An explicitly specified domain must always start
with a dot.
So if we push down:
Response.Cookies.Add(new HttpCookie("Banana", "1")
{
Domain = Request.Url.Host
});
We pushed down the host-name explicitly, we should get a domain name set on the cookie which would be prefixed with the dot, in this case it should be ".testdomain.com".
It also states what's on Wikipedia:
Domain Defaults to the request-host. (Note that there is no dot at
the beginning of request-host.)
With me so far?
If I use the first method, defining a Domain:
Response.Cookies.Add(new HttpCookie("Banana", "1")
{
Domain = Request.Url.Host
});
This is the results:
IE9: 1 cookie
Opera: 1 cookie
Firefox: 1 cookie
Chrome: 1 cookie
As you can see, both Opera and IE both set an EXPLICIT domain without the dot prefix.
Both Firefox and Chrome DO set the EXPLICIT domain with a dot prefix.
If I use the following code:
Response.Cookies.Add(new HttpCookie("Banana", "2")
{
});
IE / Opera: Both have the exact same result, the domain WITHOUT the dot prefix.
Funnily enough, Firefox and Chrome both create cookies WITHOUT the dot prefix.
(I cleared all cookies and ran the code again)
Firefox:
Chrome:
INTERESTING BIT
This is where it gets interesting. If I write the cookies one after another like so:
Response.Cookies.Add(new HttpCookie("Banana", "1")
{
Domain = Request.Url.Host
});
Response.Cookies.Add(new HttpCookie("Banana", "2")
{
});
PERSONALLY I would expect one cookie to exist in the browser, because I assume it's based on the cookie name.
Here's what i've observed:
In IE / Opera, the LAST cookie set is the cookie that is used. This is because the Cookie name and Domain name are identical.
If you explicitly define a domain name with a dot, both browser will still see 1 cookie, the last cookie of the same name.
Chrome and Firefox on the other hand, see more than 1 cookie:
I wrote the following JavaScript to dump the values to the page:
<script type="text/javascript">
(function () {
var cookies = document.cookie.split(';');
var output = "";
for (var i = 0; i < cookies.length; i++) {
output += "<li>Name " + cookies[i].split('=')[0];
output += " - Value " + cookies[i].split('=')[1] + "</li>";
}
document.write("<ul>" + output + "</ul>");
})();
</script>
These are the results:
IE - 2 cookies set (browser sees 1):
Opera - 2 cookies set (browser sees 1):
Firefox - 2 cookies set and browser sees 2!:
Chrome - 2 cookies set and browser sees 2!:
Now you're probably wondering wtf all this is.
Well:
- When you access the cookie by Name in C#, it gives you 1 cookie. (the first cookie that has that name)
- The browser sends ALL cookies to the server
- The browser doesn't send any information other than the key/value of the cookie. (this means the server doesn't care about the domain)
- You can access both cookies of the same name, if you retrieve them by index
The problem...
We had to change our Authentication to specify the domain in the cookie when we pushed it down.
This broke Chrome and Firefox, users were no longer able to login, because the server would try authenticate the old auth cookie. This is because (from my understanding) it uses the Authentication Cookie Name to retrieve the cookie.
Even tho there are two cookies, the first one is retrieved which happens to be the old one, authentication fails, user isn't logged in. SOMETIMES the correct cookie is first in the list, and the authentication succeeds...
Initially we solved this by pushing a cookie with the old domain to expire it. This worked in Chrome and Firefox.
But it now broke IE/Opera since both browsers don't care about the domain and only compare the cookie based on the name.
My conclusion is that the domain on a cookie is a complete utter waste of time.
Assuming that we must specify the domain, and we can't rely on users to clear their browser cache. How can we resolve this problem?
Update:
Digging into how .NET signs a user out.
if (FormsAuthentication._CookieDomain != null)
{
httpCookie.Domain = FormsAuthentication._CookieDomain;
}
It looks like it's entirely possible for the Forms authentication to push an expired Auth cookie, that is entirely unrelated to the cookie the user is authenticated with. It doesn't use the current Auth Cookie's domain.
Which it can't use anyway, since the domain isn't pushed back to the server with the cookie.
Update 2
It seems FormsAuthentication is really broken. If you use an explicit domain name on a cookie when you authenticate the user, wait for the session to timeout, then refresh the page, the method of generating the cookie used by FormsAuthentication results in the domain being null which causes the browser to assign a dotless domain.
It requires that Forms be assigned a domain up front for it to be assigned to the cookie, this breaks a multi-tenant system...
See Question&Answers more detail:
os