Have you often configured the authorization settings of your website to specify areas with special access rules? One of the common scenarios is to create a website that should be available only to authenticated users. When doing this, have you ever run into the strange behavior of a login page life cycle being executed multiple times when the access to all pages is denied for anonymous users? Read on to see why this happens and how you can avoid it with only a few changes in the web.config file of your website.

The basic setup

First of all, let’s have a look at the configuration I mentioned above. The following code snippet displays the usual approach for denying access for users that are not logged in, then redirecting them to a login page:

<configuration>
  
  <system.web>
    
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login.aspx" timeout="2880" />
    </authentication>
    <authorization>
      <deny users="?" />
    </authorization>
    
  </system.web>
  
</configuration>

When you run the website and get sent to the login page, you may notice something very peculiar. The whole life cycle of the page is executed more than once, which is clearly not the expected behavior and could be problematic in scenarios that rely on certain events to be fired only once.

Let’s see an example

Counting login attempts

You may want to count the number of login attempts in order to display a captcha when they become too many. The following sample shows a possible implementation of this functionality. The login attempts count is stored in ViewState and when the problem mentioned above occurs, ViewState["LoginAttempts"] will be set to 0 every time the Load event of the page is fired, thus the captcha will never appear.

TextBox username = LoginUser.FindControl("UserName") as TextBox;
        TextBox password = LoginUser.FindControl("Password") as TextBox;
        bool isUserValid = Membership.ValidateUser(username.Text, password.Text);
        if (!IsPostBack)
        {
            ViewState["LoginAttempts"] = 0;
        }
        else if (!isUserValid)
        {
            ViewState["LoginAttempts"] = (int)ViewState["LoginAttempts"] + 1;
        }
 
        if ((int)ViewState["LoginAttempts"] > 3)
        {
            DisplayCaptcha();
        }

 

RadCaptcha in this case

Another interesting case is related to one of Telerik’s ASP.NET AJAX controlsRadCaptcha. Its image is refreshed every time the page life cycle is executed. In the usual case this is reflected both in the browser and in the code-behind of the page where the actual validation takes place. Once we hit the multiple redirect scenario, however, the page itself is not refreshed on the client - the original image remains on the page, but the code-behind has already changed it several times.  This means that the code, displayed in the rendered captcha image, is not the actual one the server expects anymore. When you try to enter it in the input field, the captcha will not be validated and you will receive a message for incorrect code.

The origin of the problem

If you examine the login page’s source, you will notice that there are additional resources, which are referenced - stylesheets, JavaScript files, the favicon. A separate request to the server is performed to retrieve each one of them.

Here is how the <head> section of your page may look like:

<link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<script type="text/javascript" src="<%: ResolveUrl("~/Scripts/jquery-1.8.3.min.js") %>"></script>

Since you have denied the access to all files for unauthenticated users, ASP.NET performs a redirect to the login page when each resource is requested. This is what causes the server to go through the lifecycle of the login page several time.

How to fix the unexpected redirects

In order to avoid this, you should set permission for accessing these resources in web.config. The following example shows how to grant access for several commonly used resources that could be referenced in the login page:

<configuration>
    
  <location path="Styles/Site.css">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
  <location path="Scripts/jquery-1.8.3.min.js">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
  <location path="favicon.ico">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
  <location path="Telerik.Web.UI.WebResource.axd">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
  
</configuration>

Note the setting for the favicon in the sample above. I am mentioning this explicitly as the discussed problem is often a result of forgetting that the favicon is also requested from the server. Moreover, browsers may request it even if you do not reference it explicitly on the page.

 

Have you seen anything like that?

I hope this will be useful for properly configuring the authorization settings of your website. Has this problem plagued you before? Do you think there is a better way to do it? How would you have gone about solving it? Feel free to leave a comment to brag about a similar nasty scenario you have solved.


About the Author

Marin Bratanov

is a Principal Technical Support Engineer in the ASP.NET AJAX division. Ever since he joined Telerik in early 2011 as a novice, his main focus has been improving the services and customer care the company offers. Apart from work, Marin is an avid reader and usually enjoys the worlds of fantasy and Sci-Fi literature. You can find him on Twitter, Goodreads, LinkedIn and GooglePlus.

Related Posts

Comments