Using Forms Authentication in ASP.NET MVC Applications

Introduction

Most of the real world web applications require security in one form or another. As far as ASP.NET is concerned Forms Authentication is the most popular and common method of protecting your website from unauthorized access. ASP.NET web forms and server controls (such as Login and CreateUserWizard) make it extremely easy to implement Forms Authentication in web forms based websites. However, if you are developing an ASP.NET MVC web application you need to take care of some steps on your own. In this step-by-step tutorial you will learn to implement Forms Authentication in ASP.NET MVC web applications. You will also learn to use membership features, role based security and profile features.

Enabling SQL Server Database for Membership, Roles and Profile Features

To begin with, create a new ASP.NET MVC 3 Web Application using Visual Studio 2010. Choose the Empty project template. The Internet Application project template already includes controllers and views that make use of Forms Authentication and membership features. Since we want to learn the process step-by-step we are going to develop our example from the ground up and hence we will go with the Empty project template. Also, make sure to select the view engine as ASPX.

An empty ASP.NET MVC3 Project
Figure 1: An empty ASP.NET MVC3 Project

Once the ASP.NET MVC Web Application is created, open the ASP.NET Configuration tool from the Project menu. This will open the Web Site Administration Tool. Go to the Provider tab and click on "Select a single provider for all site management data" option. On the next screen you will see AspNetSqlProvider. Click on Test if you wish to test, otherwise click Back and close the tool.

Select a single provider for all site management data
Figure 2: Select a single provider for all site management data

Now, switch to the Security tab and create two roles - Administrator and NormalUser for testing purpose.

Switch to the Security tab and create two roles
Figure 3: Switch to the Security tab and create two roles

The above step adds a SQL Server database (ASPNETDB.mdf) in App_Data folder of your web application, pre-configured with tables required for membership, roles and profile features.

Adding a SQL Server database
Figure 4: Adding a SQL Server database

If you wish to use an external SQL Server database you will need to configure it using the aspnet_regsql.exe command line tool. Just follow the wizard presented by the tool and it will create the required tables in the specified database.

Configuring Forms Authentication in web.config

The next step is to configure your web application to enable forms authentication, membership and role based security. Open web.config and add the <authentication> section as shown below:

<authentication mode="Forms">
 <forms loginUrl="~/Membership/Login" timeout="2880" />
</authentication>

Using the <authentication> section you set the authentication scheme to Forms and login URL to ~/Membership/Login. You will be creating the Membership controller with Login action later.

Next, configure Membership and Roles providers as shown below :

<membership defaultProvider="MyMembershipProvider">

B B B  <providers>

B B B B B B B  <add name="MyMembershipProvider" 

B B B B B B B B B type="System.Web.Security.SqlMembershipProvider" 

B B B B B B B B B connectionStringName="connstr" />

B B B  </providers>

</membership>

 

<roleManager enabled="true" defaultProvider="MyRolesProvider">

B B B  <providers>

B B B B B B B  <add name="MyRolesProvider" 

B B B B B B B B B type="System.Web.Security.SqlRoleProvider" 

B B B B B B B B B connectionStringName="connstr" />

B B B  </providers>

</roleManager>

The <membership> section configures the membership provider (MyMembershipProvider) and the <roleManager> section configures the roles provider (MyRolesProvider). The connectionStringName specifies the database connection string that points to the SQL server database storing the membership and roles information.

Creating a New User

Now that you have configured your web application to use membership and roles features, let's create a controller and a couple of views that will allow users to register with the web application. In the sections that follow we won't pay much attention to validating the data for the sake of simplicity. In a real world scenario, however, you will need to add those features too.

Add a new controller class to the Controllers folder and name it as MembershipController. Add two actions viz. CreateUser() and CreateUser(CreateUserData obj) as shown below:

[HttpGet]
public ActionResult CreateUser()
{
B B B  return View();
}

[HttpPost]
public ActionResult CreateUser(CreateUserData data)
{
B B B  MembershipCreateStatus status;

B B B  Membership.CreateUser(data.UserID,data.Password,data.Email,data.Question,data.Answer,true, out status);

B B B B if (status == MembershipCreateStatus.Success)
B B B  {
B B B B B B B  ViewBag.StatusMessage = "User created successfully!";
B B B  }
B B B  else
B B B  {
B B B B B B B  ViewBag.StatusMessage = "Error creating user account!";
B B B  }
B B B  return View("CreateUserStatus");
}

Notice that the first CreateUser() action is marked with [HttpGet] attribute indicating that it is intended to be used with GET requests. It just displays CreateUser view for entering new user information.

The other version of CreateUser() action accepts a parameter of type CreateUserData and is marked with [HttpPost] attribute. This version will be invoked for POST requests. The CreateUserData class is a custom model class that wraps the form POST data and looks like this:

public class CreateUserData
{
B B B  public string FirstName { get; set; }
B B B  public string LastName { get; set; }
B B B  public string UserID { get; set; }
B B B  public string Password { get; set; }
B B B  public string Email { get; set; }
B B B  public string Question { get; set; }
B B B  public string Answer { get; set; }
}

As you can see, the CreateUserData class simply contains a series of properties. The FirstName and LastName properties will be used with profile features. The remaining properties viz. UserID, Password, Email, Question and Answer are used by ASP.NET membership features. The second version of CreateUser() action uses the ASP.NET Membership object to create a new user. The CreateUser() method of the Membership object accepts user information, such as user name and password, and returns success or failure of the registration operation via an output parameter. The output parameter is of enumeration type MembershipCreateStatus. The code checks this status value and accordingly stores a StatusMessage in the ViewBag. Finally, the CreateUserStatus view displays the status to the end user.

Now, add the CreateUser view by right clicking on CreateUser() action and then selecting Add View option.

Add CreateUser view
Figure 5: Add CreateUser view

Key-in the following HTML markup into the CreateUser view:

<form method="post" action="CreateUser">
<h1>Register</h1>
<table cellpadding="3" cellspacing="0" class="style1">
<tr>
<td align="right">First Name :</td>
<td><input name="FirstName" type="text" /></td>
</tr>
<tr>
<td align="right">Last Name :</td>
<td><input name="LastName" type="text" /></td>
</tr>
<tr>
<td align="right">User ID :</td>
<td><input name="UserId" type="text" /></td>
</tr>
<tr>
<td align="right">Password :</td>
<td>
<input name="Password" type="password" /></td>
</tr>
<tr>
<td align="right">Confirm Password :</td>
<td>
<input name="ConfirmPassword" type="password" /></td>
</tr>
<tr>
<td align="right">Email :</td>
<td><input name="Email" type="text" /></td>
</tr>
<tr>
<td align="right">Security Question :</td>
<td><input name="Question" type="text" /></td>
</tr>
<tr>
<td align="right">Security Answer :</td>
<td><input name="Answer" type="text" /></td>
</tr>
<tr>
<td align="center" colspan="2"><input id="btnSubmit" type="submit" value="Submit" /></td>
</tr>
<tr>
</table>
</form>

The CreateUser view basically renders an HTML form as shown below:

The CreateUser view renders an HTML form
Figure 6: The CreateUser view renders an HTML form

Note that though we are not making use of First Name and Last Name values in the CreateUser action we still accept these values. Later you will store these values in the profile of a user. Also notice that the various <input> fields have the same name as the respective properties of CreateUserData class. This allows the ASP.NET MVC framework to correctly map the form fields with the properties.

Also, add CreateUserStatus view and key-in the following markup into it:

<center>
<strong><%= ViewBag.StatusMessage %></strong>
<br />
<br />
<%= Html.ActionLink("Register another","CreateUser") %>
&nbsp;Or&nbsp;
<%= Html.ActionLink("Log-in","Login") %>
</center>

Notice how the above markup makes use of the StatusMessage member of the ViewBag. The CreateUserStatus view additionally renders to action links - one pointing to CreateUser action (GET version) and the other pointing to Login action. At runtime the CreateUserStatus view looks like this :

User created successfully
Figure 7: User created successfully

Before you go ahead, run the web application; navigate to /Membership/CreateUser and create two users for testing purposes (say user1 and user2). Add one of the users to Administrator role using the Web Site Administration Tool.

Add one of the users to Administrator role
Figure 8: Add one of the users to Administrator role

Authenticating Existing Users

In order to authenticate existing users, you will add two actions in MembershipController. These actions are shown below:

[HttpGet]
public ActionResult Login()
{
B B B  return View();
}
 
[HttpPost]
public ActionResult Login(LoginData data)
{
B B B  if (Membership.ValidateUser(data.UserID, data.Password))
B B B  {
B B B B B B B  bool flag = (data.RememberMe == "on" ? true : false);
B B B B B B B  FormsAuthentication.SetAuthCookie(data.UserID,flag);
B B B B B B B  return RedirectToAction("Index", "Article");
B B B  }
B B B  else
B B B  {
B B B B B B B  ViewBag.ErrorMessage = "Invalid UserID or Password!";
B B B B B B B  return View();
B B B  }
}

Just like the CreateUser() action the Login() action also has two versions - one for GET requests and the other for POST requests. The former one simply returns the Login view. The later version of the Login() action accepts a parameter of type LoginData. The LoginData class is a custom model class as shown below:

public class LoginData
{
B B B  public string UserID { get; set; }
B B B  public string Password { get; set; }
B B B  public string RememberMe { get; set; }
}

Inside, the Login() action makes use of Membership object to validate whether the user credentials are valid or not. Notice that the RememberMe property returns "on" if the remember me checkbox is checked. Accordingly, SetAuthCookie() method of the FormsAuthentication class issues a forms authentication cookie. The user is then redirected to Index() action from the Article controller (you will code it later). If the user credentials are invalid an ErrorMessage is stored in the ViewBag and Login view is rendered again.

The Login view required by the Login action is shown below:

<form method="post" action="Login">
<h1>Log-in</h1>
<table cellpadding="3" cellspacing="0" class="style1">
<tr>
<td align="right">User ID :</td>
<td><input name="UserId" type="text" /></td>
</tr>
<tr><td align="right">Password :</td>
<td><input name="Password" type="password" /></td>
</tr>
<tr>
<td align="right">Remember Me :</td>
<td><input name="RememberMe" type="checkbox" /></td>
</tr>
<tr>
<td align="center" colspan="2"><input id="btnSubmit" type="submit" value="Submit" /></td>
</tr>
<tr>
<td colspan="2" align="center">
<strong>
<%= (ViewBag.ErrorMessage == null?"":ViewBag.ErrorMessage) %>
</strong>
</td>
</tr>
</table>
<%= Html.ActionLink("New users register here","CreateUser") %>
</form>

When displayed the Login view will look like this :

The Login view
Figure 9: The Login view

To complete the login functionality, add another controller named ArticleController. The ArticleController will have the Index() action. The following code shows the Index() action.

[Authorize]
public ActionResult Index()
{
B B B  return View();
}

The Index() action is marked with [Authorize] attribute indicating that only authenticated users can invoke this action. The Index view simply contains a welcome message.

In order to test the functionality so far, run the web application and navigate to /Article/Index. You will find that since we have enabled the forms authentication, you will be automatically redirected to the Login page. You will be taken to the Index page only after entering a valid user ID and password. Also test the error message by entering some invalid user credentials.

Role Based Security

Implementing role based security is a matter of setting the Roles property of the [Authorize] attribute.

[Authorize(Roles="Administrator")]

The Roles property specifies a list of roles eligible to invoke the action under consideration. If you run the Index action after modifying the [Authorize] attribute to include the Roles property, you will be redirected to the Login page again if the current user does not belong to the Administrator role.

Storing and Retrieving Profile Information

ASP.NET Profile features allow you to capture user specific information and then render a personalized experience in web pages. In order to store and retrieve profile information for the users, you must configure the profile provider and profile properties in the web.config file. You will use the <profile> section to do so. The following markup shows the <profile> section.

<profile enabled="true" defaultProvider="MyProfileProvider">
<providers>
<add name="MyProfileProvider"
type="System.Web.Profile.SqlProfileProvider"
connectionStringName="connstr" 
applicationName="/"/>
</providers>
<properties>
<add name="FirstName" />
<add name="LastName" />
</properties>
</profile>

The enabled attribute of the <profile> section is set to true. The <properties> section defines two profile properties, namely FirstName and LastName. Recollect that you are accepting First Name and Last Name on the user registration page. You need to store those values in the profile of that user. To do so, modify the CreateUser POST action as shown below:

...
if (status == MembershipCreateStatus.Success)
{
 ProfileBase profile = ProfileBase.Create(data.UserID);
 profile["FirstName"] = data.FirstName;
 profile["LastName"] = data.LastName;
 profile.Save();
 ViewBag.StatusMessage = "User created successfully!";
}
else
{
...

Notice the lines marked in bold letters. You use ProfileBase class from System.Web.Profile namespace to get access to the profile of a specific user. The Create() static method of ProfileBase class accepts a user name whose profile is to be retrieved and returns an instance of ProfileBase class. You can then set profile properties on the ProfileBase object thus returned. Once all the properties are set, the Save() method is called to persist the profile property values in the underlying database.

You can now access the FirstName and LastName profile properties in the ArticleController.

[Authorize]
public ActionResult Index()
{
B B B  ProfileBase profile = ProfileBase.Create(Membership.GetUser().UserName);
B B B  ViewBag.DisplayName = profile["FirstName"] + " " + profile["LastName"];
B B B  return View();
}

In the code shown above, you first get ProfileBase object as before. This time, however, you use the Membership.GetUser() method to retrieve the currently logged in user. The FirstName and LastName profile properties are then retrieved and stored in ViewBag as DisplayName member. There is an alternative to the above way of accessing the user profile. You can also retrieve the profile properties like this:

string fname = HttpContext.Profile["FirstName"] as string;
string lname = HttpContext.Profile["LastName"] as string;

The HttpContext.Profile points to the ProfileBase instance for the currently logged in user. You can then access the individual profile properties as before.

The DisplayName member of the ViewBag can be accessed in the Index view as shown below:

...
<h1>Welcome <%= ViewBag.DisplayName %>!</h1>
...

Summary

Using ASP.NET Forms Authentication you can restrict the users accessing your web application. In this article you secured an ASP.NET MVC application using Forms Authentication, Membership and Roles features. The [Authorize] attribute indicates that an action can be invoked only by authenticated users. You can implement role based security by setting the Roles property of the [Authorize] attribute. Forms Authentication, Membership, Roles and Profile features together help you to secure your web application from unauthorized access and also allow you to render personalized web pages.

About the Author:

Bipin Joshi is a blogger and writes about apparently unrelated topics - Yoga & technology! A former Software Consultant by profession, Bipin is programming since 1995 and is working with .NET framework ever since its inception. He has authored or co-authored half a dozen books and numerous articles on .NET technologies. He has also penned a few books on Yoga. He was a well known technology author, trainer and an active member of Microsoft developer community before he decided to take a backseat from the mainstream IT circle and dedicate himself completely to spiritual path. Having embraced Yoga way of life he now codes for fun and writes on his blogs. He can also be reached there.



About the Author

Bipin Joshi

Bipin Joshi is a blogger and writes about apparently unrelated topics - Yoga & technology! A former Software Consultant by profession, Bipin has been programming since 1995 and has been working with the .NET framework ever since its inception. He has authored or co-authored half a dozen books and numerous articles on .NET technologies. He has also penned a few books on Yoga. He was a well known technology author, trainer and an active member of Microsoft developer community before he decided to take a backseat from the mainstream IT circle and dedicate himself completely to spiritual path. Having embraced Yoga way of life he now codes for fun and writes on his blogs. He can also be reached there.

Related Articles

Downloads