I am running up against an issue with forms authentication in ASP.NET web forms, where the login page redirects to itself for some versions of IE. I have followed the advice on similar SO questions and added cookieless="UseCookies"
to my Web.config, but it has made no difference, and you can see that the server is indeed trying to set cookies for every request.
The server is running Widows Server 2008 R2 Enterprise SP1 with .NET Framework 4.5.1 fully updated as of today (2/16/14). I used netcat as my client to simulate various browsers, so that I could be sure there was no extra browser state involved.
Here are the relevant bits from my Web.confg:
<authentication mode="Forms">
<forms name="DevApp"
loginUrl="Login.aspx"
protection="All"
timeout="43200"
path="/"
requireSSL="false"
slidingExpiration="true"
defaultUrl="default.aspx"
cookieless="UseCookies" />
</authentication>
<authorization>
<deny users="?" />
</authorization>
<location path="Login.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
Below are my test cases for each version of IE from 7 to 11. Note the 302 redirect for 7, 8, and 11; back to the same page requested.
IE 7
bmitchell@dropbear:~$ cat login.ie7.txt
GET /Login.aspx HTTP/1.1
Host: dev-app
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1)
bmitchell@dropbear:~$ nc dev-app 80 -q 1 < login.ie7.txt | head -15
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Login.aspx
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: ASP.NET_SessionId=fv5hajqvudwenz2ravfcmk4a; path=/; HttpOnly
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 20:15:01 GMT
Content-Length: 128
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Login.aspx">here</a>.</h2>
</body></html>
bmitchell@dropbear:~$
IE 8
bmitchell@dropbear:~$ cat login.ie8.txt
GET /Login.aspx HTTP/1.1
Host: dev-app
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)
bmitchell@dropbear:~$ nc dev-app 80 -q 1 < login.ie8.txt | head -15
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Login.aspx
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: ASP.NET_SessionId=fof1s10au0z4ygtjvhstetar; path=/; HttpOnly
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 20:15:28 GMT
Content-Length: 128
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Login.aspx">here</a>.</h2>
</body></html>
bmitchell@dropbear:~$
IE 9
bmitchell@dropbear:~$ cat login.ie9.txt
GET /Login.aspx HTTP/1.1
Host: dev-app
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
bmitchell@dropbear:~$ nc dev-app 80 -q 1 < login.ie9.txt | head -15
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: ASP.NET_SessionId=ytmeqzxvammbvj5ege1exyv5; path=/; HttpOnly
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 20:16:01 GMT
Content-Length: 13542
<!DOCTYPE html>
<html>
bmitchell@dropbear:~$
IE 10
bmitchell@dropbear:~$ cat login.ie10.txt
GET /Login.aspx HTTP/1.1
Host: dev-app
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)
bmitchell@dropbear:~$ nc dev-app 80 -q 1 < login.ie10.txt | head -15
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: ASP.NET_SessionId=uuikncdmso04kskr0vxq2vkn; path=/; HttpOnly
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 20:16:25 GMT
Content-Length: 13542
<!DOCTYPE html>
<html>
bmitchell@dropbear:~$
IE 11
bmitchell@dropbear:~$ cat login.ie11.txt
GET /Login.aspx HTTP/1.1
Host: dev-app
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
bmitchell@dropbear:~$ nc dev-app 80 -q 1 < login.ie11.txt | head -15
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Login.aspx
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: ASP.NET_SessionId=uwsde4ccrslvehr415fcltkc; path=/; HttpOnly
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 20:17:33 GMT
Content-Length: 128
<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Login.aspx">here</a>.</h2>
</body></html>
bmitchell@dropbear:~$
The user agent strings were taken from posts on IEBlog, | head -15
was used to truncate the output from the server at 15 lines. I had the same results using real IE 8 through 11, I did not have an IE 7 install handy. I also tried the same requests, but included a ASP.NET_SessionId cookie obtained from the server, but the results were identical.
Update: here is the fiddler reported headers for a successful login from both the initial request, and the following post, for IE 11 masquerading as IE 10.
GET http://dev-app/Login.aspx HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)
Accept-Encoding: gzip, deflate
Host: dev-app
DNT: 1
Connection: Keep-Alive
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: ASP.NET_SessionId=kea0fsuzfjgh54kmwfdgbl4k; path=/; HttpOnly
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 21:43:26 GMT
Content-Length: 3845
POST http://dev-app/Login.aspx HTTP/1.1
Accept: */*
X-Requested-With: XMLHttpRequest
X-MicrosoftAjax: Delta=true
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Referer: http://dev-app/Login.aspx
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)
Host: dev-app
Content-Length: 727
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
Cookie: ASP.NET_SessionId=kea0fsuzfjgh54kmwfdgbl4k
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Content-Encoding: gzip
Expires: -1
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: DevApp=5B53781B0160AFC2BF1D4908971283B8AB39FD803995D6FFDF2BF8474CA9472CE3C1D87BE59BAA4B543C913F045FAB1A5D4FE7C64AAE16BAAEF0E87232E70519A4EE9EAB9CA9DDE824CF1315EBB64AEA01E0814112A3FE858F11C3644FD041ED07C072A3B4DD7C4CBF3ED688A997F7071386F4456715F42EDD10B695F0CE9F4A461C12501C51899B9089870C567F681EF6BC8D18; expires=Fri, 28-Mar-2014 21:43:38 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 21:43:38 GMT
Content-Length: 2422
The authentication cookie (DevApp) is cleared when the login page is initially loaded, and then set when credentials are posted back to the page.
Update: here is the fiddler reported headers for a failed login attempt for IE 11.
GET http://dev-app/Login.aspx HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: dev-app
DNT: 1
Connection: Keep-Alive
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Login.aspx
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: ASP.NET_SessionId=4fnaiqvqgqdgnmpudhzuhx0j; path=/; HttpOnly
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 22:28:32 GMT
Content-Length: 128
GET http://dev-app/Login.aspx HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: dev-app
Cookie: ASP.NET_SessionId=4fnaiqvqgqdgnmpudhzuhx0j
Connection: Keep-Alive
DNT: 1
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /Login.aspx
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Set-Cookie: DevApp=; expires=Tue, 12-Oct-1999 07:00:00 GMT; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 22:28:32 GMT
Content-Length: 128
The last request/response is repeated about 120 times until the browser gives up.
Update: Solved! Thanks to @Wiktor Zychla and @dana for pushing me to look into the code, even though I was (wrongly) convinced it was a configuration/environmental issue. That will teach me not to trust inherited code that hasn't been touched in years.
The question of why the auth cookie was being removed turned out to be pretty simple, the login page has an OnLoad
override that looks like this:
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
if(!IsPostBack) {
FormsAuthentication.SignOut();
//...
}
}
The call to FormsAuthentication.SignOut
does the cookie removal. Unusual, and likely unnecessary, but not the source of the problem.
The real issue was further down in the method with this bit of code:
System.Web.HttpBrowserCapabilities browser = Request.Browser;
double version = 0.0d;
Double.TryParse(browser.Version, out version);
if(browser.Browser != "Firefox"
&& browser.Browser != "Chrome"
&& !(browser.Browser == "IE" && version >= 9.0)) {
BrowserMessageDiv.Visible = true;
BrowserMessage.Text = GetBrowserMessage();
} else {
BrowserMessageDiv.Visible = false;
}
This is attempting to display a message on the login page for browsers that are not Firefox, Chrome, or IE >= 9 that advises which browsers work best with this site. The "bad" redirect is coming from the GetBrowserMessage
call, which attempts to look up user language preferences for the message text to display, but uses a mechanism that requires the user to be logged in, and as such, redirects back to the login page. Fail!
The reason the issue just came up when testing IE 11 is quite baffling from a "Why did they do that?" perspective. Request.Browser.Browser
reports "IE"
for IE versions up to 10, but "InternetExplorer"
for IE 11!