Role Based Forms Authentication in ASP.NET 2.0
 
Published: 01 Apr 2008
Abstract
Using Role based forms authentication, we can restrict users of the site to accessing certain resource if they are not part of a particular role. In this article, Satheesh demonstrates how to build sites with this type of authentication. He provides a short overview of various Login Controls and Providers and then discusses a scenario with detailed explanation of various aspects of the sample application with screenshots and source code.
by Satheesh Babu
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 65614/ 89

Introduction

Any ASP.NET site we develop will always have an authentication module unless and until there is nothing private on the site or something we explicitly know as being unnecessary. For instance, a simple public content site will not require an authentication module. Depending on the nature of the application we can decide on the type of authentication we are going to provide for our site. Meaning, if it is going to be an intranet web application, we can very well utilize the windows authentication by making use of the availability of user information in the AD, thus preventing a separate user information storage private to our application. Still, role mapping to the users has to be stored privately for the application. If our application is an internet application then the best choice is to use Forms Authentication. This article will explore the implementation forms authentication with roles in ASP.NET 2.0.

From 1.x days, the implementation of forms authentication in ASP.NET is not that complicated. But the actual drawbacks are in those days ASP.NET itself does not have that many controls that aid in implementing forms authentication easily. Also, if we have role based forms authentication then we should go for our custom code for validating a resource access. With the introduction of 2.0 we have a handful of controls that help in implementing role based forms authentication very easily with the primary support of Providers in ASP.NET 2.0.

Who Process the FormsAuthentication?

Forms authentication is processed by a HTTP module called FormsAuthenticationModule which takes part in regular ASP.NET page processing.

Login Controls and Providers

As I said earlier, ASP.NET 2.0 is packed with set of new controls called Login controls through which we can implement forms authentication with less effort. This section will take us through how best to use these Login controls in our projects. By default, Login controls will use the default membership provider and role provider which will have their own database schema. So, it is not advisable to use this controls as it is because enterprise applications itself will have its own database schema and architecture. The solution for this problem is to develop our own custom provider or our own custom code that solves our application specific requirements. However, we can still use some of the login controls, like LoginView, LoginName and LoginStatus, in our applications. In this article I will explain our subject matter with a custom implemented login form without using any login control on it. Also, we will have a look on how to use some of the login controls effectively with role based forms authentication. Moving forward, I will explain a sample scenario where we can use role based forms authentication and will use the same scenario throughout this article for easy understanding.

Scenario

Consider we are creating a content management system where users can login and post their contents. The posted contents will be moderated by the administrator of the site. Meaning, Administrator can either approve or reject the content posted by the user. The posted content can be viewed by anyone once it is approved by the admin. In this scenario we can have 2 roles defined, namely Admin and Publishers. The sample application attached with this article uses the same scenario explained in this article.

Application Structure

Our application will have two folders called Admin and Publisher as shown in the figure below (Figure 1 – Sample Application Solution). Admin folder will have all the ASPX pages that are put together to form the Admin module while the Publisher folder will have all the ASPX pages for Publisher module.

Figure 1 – Sample Application Solution

Configuring Forms Authentication

As we all know, this is done in Web.config by changing the value of <authentication> tag.

Listing 1

<authentication mode="Forms">
  <forms loginUrl="Login.aspx" 
  name=".ASPXAUTH"     
  protection="All">
  </forms>
</authentication>

The default timeout for forms authentication is 30 minutes. It indicates that 30 minutes of inactivity on the application will cause timeout expiration and the user will be prompted to the login page. Any hit to the site after login will reset this clock to 30 minutes again starting from that time. If we want to override this setting then we can include this in the above <forms> tag. See MSDN for a full list of attributes that can be specified in this tag.

After configuring forms authentication we need to configure the authorization part of the Web.Config.

Listing 2

<authorization>
  <allow users="*"/> 
</authorization>

The above setting says it will allow all the users because the posted content should be viewed by anyone as I said in the Scenario section. The next section will explain the configuration settings to restrict users accessing the pages in Admin and Publisher folders.

Controlling Access to Roles

This is done by <Location> tag in Web.Config file.

Listing 3

<location path="ADMIN">
    <system.web>
      <authorization>
        <allow roles="ADMIN"/>
        <deny users="*"/>
      </authorization>
    </system.web>
  </location>
  <location path="Publishers">
    <system.web>
      <authorization>
        <allow roles="PUBLISHER,ADMIN"/>
        <deny users="*"/>
      </authorization>
    </system.web>
  </location>

The above setting will restrict users trying to access the Admin section and Publisher section until they are part of that role and allow users who are already part of the roles.

Constructing Login Form

Construct a login form that has a textbox for entering user ID and password, and a button for login with an optional Remember me checkbox.

On Login button click do the following steps.

1.    Create Forms Authentication ticket.

Listing 4 – FormsAuthentication ticket syntax

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
int version,
string userName,
DateTime CreationTime,
DateTime Expiration,
bool IsPersistent, 
string UserData,
string CookiePath);

2.    User’s role information can be specified in UserData in the above argument list.

3.    Encrypt the above created ticket through following method in FormsAuthentication class.

Listing 5 - Encrypt Ticket

string Encrypt(FormsAuthenticationTicket ticket);

4.    It returns a string containing an encrypted forms-authentication ticket suitable for use in an HTTP cookie.

5.    Create the cookie with the encrypted.

6.    Add the created cookie to the response object.

7.    The below code Listing 6 shows the implementation of the above steps.

Listing 6 - Login Event

protected void btnLogin_Click(object sender, EventArgs e)
{
  User _user = new User();
  DBOperations dbo = new DBOperations();
  _user = dbo.CheckUser(txtUserid.Text);
 
  if (_user != null)
  {
    if (_user.Password == txtPassword.Text)
    {
      FormsAuthenticationTicket Authticket = new FormsAuthenticationTicket(1,
        txtUserid.Text, DateTime.Now, DateTime.Now.AddMinutes(30),
        chkRememberMe.Checked, _user.Role, FormsAuthentication.FormsCookiePath);
 
      string hash = FormsAuthentication. Encrypt(Authticket);
 
      HttpCookie Authcookie = new HttpCookie
        (FormsAuthentication.FormsCookieName, hash);
 
      if (Authticket.IsPersistent)
        Authcookie.Expires = Authticket.Expiration;
 
      Response.Cookies.Add(Authcookie);
 
      string returnUrl = Request.QueryString["ReturnUrl"];
      if (returnUrl == null)
        returnUrl = "/";
 
      Response.Redirect(returnUrl);
    }
    else
    {
      lblMessage.Text = "Password does'nt match.";
    }
  }
  else
  {
    lblMessage.Text = "User not exists.";
  }
}

Since the user information is stored as encrypted value in the cookie we need to construct the decrypted version of our credentials for every request and assign it to the Context object. This is done to make the user information available on the pages. The FormsAuthentication module will decrypt the forms authentication ticket in the cookie and make it available through the property HttpContext.Current.User.Identity. A new GenericPrincipal object should be constructed and assigned to the User property of Context object. This has to be done in Application_AuthenticateRequest event in Global.asax file. By default, there will be no Global.asax file added to our solution if you use visual studio 2005 so we need to add it explicitly through "Add new Item."

Listing 7 - Application Authenticate Event

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
  if (HttpContext.Current.User != null)
  {
    if (HttpContext.Current.User.Identity.IsAuthenticated)
    {
      if (HttpContext.Current.User.Identity is FormsIdentity)
      {
        FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
        FormsAuthenticationTicket ticket = id.Ticket;
        string userInfo = ticket.UserData;
        string[]roles = userInfo.Split(',');
        HttpContext.Current.User = new GenericPrincipal(id, roles);
      }
    }
  }
}

We need to import System.Security.Principal namespace to the Global.asax file for the above code to work.

Listing 8 - Import Namespace

<%@ Import Namespace="System.Security.Principal" %>
Adding Remember Me Option

In the above code the line that creates FormsAuthentication ticket has an argument for specifying cookie as a persistent or non persistent cookie. This attribute is the one that is used for "Remember Me" option in Login pages. Include a Checkbox beneath the password textbox in the login page and assign the Checked property of the checkbox as the value for the above said argument.

We have completed creating a simple site with role based forms authentication. Now when we run our application, users with admin role can access the resources in admin section publisher section. A user with a publisher role cannot access admin resources while he can access the Publisher section.

In the next section we will see how to use the LoginView control to show and hide a group of controls on a page.

Using LoginView Control with Roles

It can be used to show or hide a group of control or content based on the user login status.

Below is the syntax of using it.

Listing 9 - LoginView Control

<asp:LoginView ID="LoginView1" runat="server">
    <LoggedInTemplate>
    </LoggedInTemplate>
    <AnonymousTemplate>
    </AnonymousTemplate>
</asp:LoginView>

Things inside <LoggedInTemplate> will be exposed only if the user is already logged in where the things in <AnonymousTemplate> will be visible even if the user is not logged in.

For example, if we want to display or hide controls based on the user's role then this control is a champion of doing it with very little effort. Consider we have an error page that reports the error to the users. If we want to hide the technical error message to all the users and show the technical error message only for the users with ADMIN role then we can use LoginView control to achieve this requirement.

Refer to the below code for the implementation.

Listing 10 - LoginView Control With Roles

<asp:LoginView ID="loginview" runat="server">
    <RoleGroups>
    <asp:RoleGroup Roles="ADMIN">
    <ContentTemplate>
        <b>Technical Information:</b><%= TechnicalErrorMsg %>
    </ContentTemplate> 
    </asp:RoleGroup>
    </RoleGroups> 
</asp:LoginView>

The above code will display the technical error messages only to the users with ADMIN Role.

We can also achieve the above with the following code.

Listing 11 - Role Chech In Code

if (User.IsInRole("ADMIN"))
lblMessage.Text = TechnicalErrorMsg;

The next sections will describe some of the common problems that we will come across when we use FormsAuthentication and the workarounds to those problems.

Redirecting user to NotAuthorized page

When a user who does not belong to admin role is already logged in and has tried accessing the admin section, unfortunately he will be again forwarded to the Login page. Refer to Figure 2.

Figure 2 – NotAuthorized Problem

Instead, if a "Not authorized" page is displayed, it will be more appropriate for this scenario.

We can achieve this by redirecting the user to NotAuthorized page by including the below code in Page_Load event of Login page.

Listing 12 - Not Authorized Page Redirection

if (User.Identity.IsAuthenticated && Request.QueryString["ReturnUrl"] != null)
{
  Response.Redirect("NotAuthorized.aspx");
}

The above code checks whether the user is authenticated and ReturnUrl is not null through which we can confirm that the user is trying to access a resource which he does not have access after authenticating.

FormsAuthentication.SignOut() doesnot reflect in LoginStatus and LoginName

Suppose, if we have a password reset feature in our ASP.NET site then the user should be forced to logged out once he chooses the new password, and clicked save. He should be asked to login again and verify the new password.

When a user is already logged in, he can reach the form PasswordReset.aspx and he can choose the new password. When the user types in the new password and clicks save, the user is logged off by calling SignOut() method of FormsAuthentication object. Refer to the code below.

Listing 13 - FormsAuthentication Signout Problem

int res = userDAO.ResetPassword(User.Identity.Name, txtOldPassword.Text, 
txtConfirmPassword.Text);
lblMessage.Text = SUCCESSMSG;
FormsAuthentication.SignOut();

The actual problem is here. After the password reset is successful the above code will log out the user as planned. But the LoginStatus and LoginName controls on the page will still show the status of the user as logged in as seen in Figure 3.

Figure 3 – Signout Problem

When the form is refreshed or for the subsequent server trip the status of the user that is displayed on the form will be successfully changed to logged out. The cause for this problem is even after the FormsAuthentication.SignOut(); line execution the context information of the current request will still hold the logged in user information. On executing the line Context.User.Identity.Name, the signout code will still give the logged in user information and thus LoginStatus control will not reflect the logout change. From the next request Context.User.Identity.Name will have no value in it which will make the login control to behave correctly.

The work around for the above problem will be making the User attribute of Context object to null.

The final code is:

Listing 14 - FormsAuthentication Signout Problem Resolution

int res = userDAO.ResetPassword(User.Identity.Name, txtOldPassword.Text, 
txtConfirmPassword.Text);
lblMessage.Text = SUCCESSMSG;
FormsAuthentication.SignOut();
Context.User = null;

The other way of solving this problem is by redirecting the user to the login page after changing the password. But you need to have some logic to notify the user that the password is successfully changed.

How to use the source code attached with this article

Unzip the code, open it with Visual Studio 2005 and hit F5 to run. The code uses database attached in APP_Data, so you need to change any setting in Web.Config. The database has already 2 user ID's created, test1 for ADMIN role and test2 for Publisher role, passwords are the same as user ID. Download the code to understand it better.

Downloads

Reference

Conclusion

We have learned implementation of role based forms authentication in ASP.NET 2.0 with a real time example. This article also provides some of the tips to use the login controls efficiently in role based forms authentication. Download the code packed with this article and see it in action.

Happy Coding!!!



User Comments

Title: it works at once   
Name: snopbear
Date: 2008-08-26 9:32:07 AM
Comment:
When I log in with test1 or any of them, I get redirected to a HTTP Error 404 - Not Found page. Any ideas?
Title: Question   
Name: Jeff
Date: 2008-06-26 9:53:38 AM
Comment:
When you log into the site with a user, go to their homepage, then click logout, hit the BACK button enough, it will get you into the home page without credentials.

Once you hit refresh it kicks you out and you have to login again.

This happens in IE6. It seems to work in Firefox right.
Title: Something is wrong   
Name: Mike
Date: 2008-06-25 10:16:43 AM
Comment:
When I log in with test1 or any of them, I get redirected to a HTTP Error 404 - Not Found page. Any ideas?
Title: permissions and roles   
Name: tariq
Date: 2008-05-12 5:39:03 AM
Comment:
Hi,

I need to grant users with permissions depending on their roles. I have heard about Visual Guard .Net http://www.visual-guard.com/EN. before I start testing, do you have any feedback about this tool?

thank you
Title: Thnx   
Name: Dhaval Patel
Date: 2008-05-02 11:04:11 AM
Comment:
thanks buddy... it helped understanding it better.






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-05-02 10:10:54 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search