Welcome to the ASP.NET Support Voice column! My name is
Jerry Orman. I have been with Microsoft over 5 years, and have spent most of my
time focused on Web-related technologies such as Microsoft FrontPage and the
new Microsoft SharePoint technologies. I have spent the last year working with
Microsoft ASP.NET as a support engineer. This month in the Support Voice
column, I am going to explain how to troubleshoot Forms Authentication in
Microsoft ASP.NET.
Troubleshooting Forms Authentication
When you use Forms Authentication in an ASP.NET application,
you may find it necessary to troubleshoot a problem that occurs when the user is
randomly redirected to the login page. In an ideal world, this
problem would occur in a manner that would let you easily attach a
debugger and capture the problem. In production environments, however, this is rarely
the case. To troubleshoot a random problem like this one, you need to log information related to the problem so that you can narrow down the root
cause.
In this column, we'll briefly cover the
Forms Authentication concept. We'll then look into which scenarios
lead to a user being redirected to the login page and how to capture data
that is relevant to isolating the problem. We'll also cover how to implement an IHttpModule interface to log the Forms Authentication information.
Forms Authentication Overview
When a user authenticates to a Web site by using Forms Authentication,
the server creates a cookie. The value of the cookie is an encrypted forms authentication ticket. The cookie is passed to the server on each request to
the application, and the
FormsAuthenticationModule class decrypts the cookie value and
determines if the user is valid or not.
By default, the
FormsAuthenticationModule
class is added in the Machine.config file. The
FormsAuthenticationModule class manages the FormsAuthentication process.
The following is an entry from the Machine.config file:
<httpModule>
.other modules.
<add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule" />
.other modules.
</httpModule>
The general HTTP traffic for authenticating by using Forms Authentication looks similar to the following:
- The client sends an HTTP GET to Default.aspx. No forms authentication cookie is sent.
- The server sends a 302 response (redirect) to Login.aspx.
- The client sends an HTTP POST to Login.aspx. It includes the login information.
- The server sends a 302 response (redirect) to Default.aspx. The forms authentication cookie is included.
- The client sends an HTTP GET to Default.aspx. This includes the forms authentication cookie.
For more information about implementing and using
forms authentication, visit the following MSDN Web sites:
For more information about sharing forms authentication cookies, visit the following ASP.NET Web site:
Reasons that a user may be redirected to the login page
The forms authentication cookie is lost
Scenario 1
In this scenario, a user logs on to the Web site. At some point, the client sends a request to the server, and the
FormsAuthenticationModule class does not receive the cookie. You can
determine if a user request does not contain the cookie by enabling cookie
logging in Microsoft Internet Information Services (IIS). To do so, follow these steps:
- Open the IIS Microsoft Management Console (MMC).
- Right-click the Web site, and then click
Properties.
- Click the Web Site tab, and then click Enable
Logging.
- Make sure that the log format is W3C Extended Log File
Format.
- Click Properties.
- Click the Advanced tab, and then click
Extended Properties.
- Under Extended Properties, click to select the Cookie(cs(Cookie)) check box and the Referer
(cs(Referer)) check box.
After this problem occurs, determine which client had the
problem and that client's IP address. Filter the IIS log on that client's IP address,
and view the <
cookie> column.
Note You can use Log Parser to parse the IIS Logs. To download Log Parser, visit the following Microsoft Web site:
After you have the list of requests from that specific
user, search for the requests to the login page. You know they were redirected
to this page, and you want to see the requests before the
redirection occurred. If you see something similar to the following, the client
either did not send the cookie or the cookie was removed on the network between the client
and server.
This is the initial login.
Method | Page | Response | Cookies |
GET | /Default.aspx | 302 (Redirect) | No
Cookies |
GET | /Login.aspx | 200 (Success) | No
Cookies |
POST | /Login.aspx | 302 (Redirect) | No
Cookies |
GET | /Default.aspx | 200
(Success) | .ASPXAUTH |
GET | /SomePage.aspx | 302 (Redirect) | No
.ASPXAUTH Cookie |
These are other requests, followed by a request to a page on the site
without the .ASPXAUTH cookie.
Method | Page | Response | Cookies |
GET | /SomePage.aspx | 302 (Redirect) | No
.ASPXAUTH Cookie |
GET | /Login.aspx | 200 (Success) | No
.ASPXAUTH Cookie |
POST | /Login.aspx | 302 (Redirect) | No
.ASPXAUTH Cookie |
GET | /SomePage.aspx | 200
(Success) | .ASPXAUTH |
Note The first request from that user is not likely to have a forms
authentication cookie unless you are creating a persistent cookie. The IIS Log will only show you the cookies that
were received in the request. The first request to have the forms authentication cookie will be on the request after a successful
login attempt.
Scenario 2
The forms authentication cookie can also be lost when the client's cookie limit is exceeded. In Microsoft
Internet Explorer, there is a limit of 20 cookies. After the 20th cookie is
created on the client, previous cookies are removed from the client's
collection. If the .ASPXAUTH cookie is removed, the user will be redirected to the login page when the next request is processed.
You can troubleshoot these two scenarios in the same way. Look at the request just
before the redirection to the login page. If the request to this page generates
cookies, this will be something to investigate.
For more information, click the following article number to view the article in the Microsoft Knowledge Base:
306070
Number and size limits of a cookie in Internet Explorer
You can use Fiddler to view the HTTP headers
that are sent to the client. After you capture the traffic, double-click a request,
and then click
Headers to see the Set-Cookie header. If you trace a
successful login, you will see the Set-Cookie header in the response of a
successful login.
To download Fiddler, visit the following Fiddler Web site:
Scenario 3
After the request leaves the client, there are various layers
that can affect the packets that are being sent. To determine if a network device is
removing the cookie, you have to capture a network trace on the client and the server,
and then look in the body of the request for the cookie. You want to
look at the client request to make sure that the cookie was sent, and check the server
trace to make sure that the server received the cookie.
Client requestThis is a GET request after the user has been authenticated. The
forms authentication ticket information is highlighted in blue. This confirms
that the cookie information left the client. When you use a network capture
tool, like Netmon, you see the traffic that actually went through the
adapter.
47 45 54 20 68 74 74 70-3a 2f 2f 6c 6f 63 61 6c GET http://local
68 6f 73 74 2f 46 6f 72-6d 73 41 75 74 68 4c 6f host/FormsAuthLo
67 54 65 73 74 2f 57 65-62 46 6f 72 6d 31 2e 61 gTest/WebForm1.a
73 70 78 20 48 54 54 50-2f 31 2e 31 0d 0a 41 63 spx HTTP/1.1..Ac
63 65 70 74 3a 20 69 6d-61 67 65 2f 67 69 66 2c cept: image/gif,
.Other headers of the GET request.
63 68 65 0d 0a 43 6f 6f-6b 69 65 3a 20 2e 41 53 che..Cookie: .AS
50 58 41 55 54 48 3d 33-43 45 46 39 42 39 41 30 PXAUTH=3CEF9B9A0
43 33 37 41 44 46 36 33-45 36 42 44 33 37 42 36 C37ADF63E6BD37B6
39 43 44 41 32 35 30 30-30 46 38 30 37 32 38 46 9CDA25000F80728F
35 31 43 39 35 36 36 44-31 34 43 35 34 31 34 35 51C9566D14C54145
38 31 43 39 33 45 32 41-30 31 44 44 43 44 45 46 81C93E2A01DDCDEF
32 34 41 31 37 34 32 39-34 31 30 43 30 39 37 34 24A17429410C0974
42 33 45 43 42 30 36 34-32 32 38 45 33 35 33 39 B3ECB064228E3539
39 41 38 32 32 42 33 42-39 33 36 44 46 30 38 46 9A822B3B936DF08F
42 41 42 44 33 45 31 30-32 44 30 30 32 31 30 43 BABD3E102D00210C
32 45 31 33 39 38 30 37-39 42 32 33 35 32 39 46 2E1398079B23529F
34 46 35 44 37 34 41 3b-20 50 72 6f 66 69 6c 65 4F5D74A; Profile
3d 56 69 73 69 74 6f 72-49 64 3d 62 32 34 65 62 =VisitorId=b24eb
Server-side requestWhen you look at the request that reached the server, you
want make sure that the server received the same information that the
client sent. If the server did not receive the same information, you need to investigate other devices on the
network to determine where the cookie was removed.
Note There have also been instances of ISAPI filters removing cookies. If you confirm that the Web server received the cookie, but the cookie is not listed
in the IIS logs, check the ISAPI filters. You may have to remove the filters to see if the
problem is resolved.
The forms authentication ticket times out
The other common cause for a user to be redirected is if the
forms authentication
ticket has expired. The forms authentication
ticket can time out in two ways. The first scenario occurs if you use
absolute expiration. With absolute expiration, the authentication ticket expires when the
expiration time expires. For example, you set an expiration of 20 minutes, and a user
visits the site at 2:00 PM. The user will be redirected to the login page if
the user visits the site after 2:20 PM.
If you use sliding expiration, the
scenario is a bit more complicated. The cookie and the resulting ticket are
updated if the user visits the site after the expiration time is half-expired. For example, you set an expiration of 20 minutes by using sliding expiration. A user visits the site at 2:00 PM, and the user receives a cookie that is set to expire at 2:20 PM. The expiration is only updated if the user visits the site after 2:10 PM. If the user visits the site at 2:09 PM, the ticket is not updated because half of
the expiration time has not passed. If the user then waits 12 minutes, visiting the site at 2:21 PM,
the ticket will be expired. The user is redirected to the login
page.
One way to approach this type of issue is to log the forms
authentication cookie and ticket information. This way, you can see if the
cookie was received by IIS and what the values are. You can do this by writing
an
HttpModule, and then plugging that module into the request pipeline. You will not have to modify your application's code to get the information you
need.
The attached sample works in the Microsoft .NET Framework 1.1 and the .NET Framework 2.0 and has
comments throughout. The sample includes the following files:
- FormsAuthEvents.cs: The class that implements IHttpModule and ties into the Application_BeginRequest event.
- FormsAuthInfo.cs: The class that retrieves the cookie and
decrypts the forms authentication ticket. It also checks the application's
Web.config file to ensure that forms authentication is enabled.
- FormsAuthConfig.cs: The class that reads information from the
FormsAuthLogger.config file.
- Log.cs: The file that accepts a stringbuilder and writes the values out
to a log file.
- FormsAuthLogger.config: The XML file that is read by the Log.cs file. This
file has to be in the /bin folder with the Built DLL. The file allows you to
configure the following:
- Filter by IP: You can filter the capture of data by
client IP. This way, you can log only requests from a client that is known to
reproduce the issue. This reduces the size of the log.
- Capture Type: This specifies where to save the file. The default is the Temporary ASP.NET Files folder, but you can save this
anywhere as long as the worker process account has the ability to write to the
folder.
Note I'll provide a download link for the code supplied in the
FormsAuthLogger.zip file.
I'll point out the main areas here:
- Create a class that implements the IHttpModule interface.
public class FormsAuthEvents : IHttpModule
{
.code.
}
- Wire up the event you want to look at. In this sample,
we're using the Application_BeginRequest event. This way we can investigate each request and determine if
it has the forms authentication cookie and log the FormsAuthenticationTicket if the cookie is there.
public void Init(HttpApplication application)
{
//Wire up the BeginRequest event
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
}
- Implement the Application_BeginRequest event.
private void Application_BeginRequest(Object source, EventArgs e)
{
.code to log the ticket.
}
- Retrieve the forms authentication cookie, and then decrypt
it.
- Log the values. I would recommend logging the following in
addition to the forms information. This will help you line up your forms
authentication information to the IIS logs, if necessary:
- Date: Allows you to see when the request came
in.
- RequestType: Shows whether the request is a Get or a
Post.
- URL: Shows the pattern of
requests leading up to the problem.
- Referrer
- ClientIP: Ties in the requests to a specific
client.