Introduction
The authentication in ASP.NET is quite easy to setup. By just adding the WebMatrix.WebData NuGet package you have access to the out-of-the-box SimpleMembershipProvider. However, I always ended up copying and pasting the code from the internet application template for an ASP.NET MVC4 application. In this article I would like to learn you how to set up and use the SimpleMembershipProvider by using the WebSecurity Utility class.
This article assumes you are familiar with concepts like Dependencies, Dependency Injection, and Test-Driven Development.
Our application
The application will be system where the user can track his worked hours on different kind of projects. To register worked hours, the user needs to login. The application can't be used with any credentials. So the basic requirements are.
- The user requires credentials.
- Users can have different roles in the application like a manager or a developer.
- Only a manager can create project codes.
These are the basic requirements for our application:
Setup the project
Let's start with setup up a project. We are going to setup with an empty ASP.NET MVC4 Web application. We will use an empty project so we can demonstrate the effectiveness from the SimpeMembershipProvider. To use the SimpleMembershipProvider, we also need to add the Entity Framework NuGet Package. We will use this package to setup our employees and departments.
- Create an empty ASP.NET MVC4 application.
- Add the WebMatrix.WebData NuGet Package.
- Add the Entity Framework Nuget Package
- Add an HomeController and an Index Page.
These are the basis setup tasks we need to do before we can start developing our solution.
Setup the database
First we will need to setup-up our database with Entity Framework. We will create two basic classes containing information about our employees and departments. In the entities we will use data annotations to specify primary keys. We will also setup our DbContext
class containing the tables or DbSet of employee and department. After that we will use the 'Enable-Migrations' command in the NuGet Package manager console to create our database for us.
The employee class:
public class Employee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string Name { get; set; }
public Department Department { get; set; }
}
The department class:
public class Department
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DepartmentId { get; set; }
public string Name { get; set; }
}
And the ArticleContext
class:
public class ArticleContext : DbContext
{
public ArticleContext() : base("DefaultConnection")
{
}
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
}
By enabling migrations for Entity Framework, a configuration class will be generated. Each time you edit or add an entity to the database, you can update your database automatically by running the 'Add-Migration [name_of_change]' and the 'update-database' command in the Package Manager Console. However this requires us to have the 'DefaultConnection
' connection string that is setup in our Web.config file. Mine looks like this.
<add name="DefaultConnection"
connectionString="Server=(local)\SQLINSTANCE;Database=ArticleDB;
Integrated Security=true;" providerName="System.Data.SqlClient"/>
We completed the follow tasks.
- We have setup our project with the necessary NuGet Packages.
- We have created classes and enabled migrations for our database.
If everything went right you should have a project looking like this, and a database created with the name 'ArticleDb'.
WebSecurity and unit testing
Now that we have setup our project, we can start finally start coding features for the user. First I would like to talk a little bit more about WebSecurity and the ASP.NET membership provider. WebSecurity
is a helper class that interacts with the ASP.NET membership provider. WebSecurity
provides us a lot of methods that are already implemented for us. Some example are: checking if the user exists, logging in the user, getting the username, etc. However, not all features from WebSecurity are implemented. Because, by default WebSecurity
uses the SimpleMembershipProvider
class. Roles are also not supported by WebSecurity
. And this kind of feature are vital for our application. How to use Roles and the static Roles
class, will be detailed in a future blog. An example how to use the WebSecurity
class.
public ActionResult Login(Credentials credentials)
{
if (WebSecurity.UserExists(credentials.UserName))
{
WebSecurity.Login(credentials.UserName, credentials.Password);
}
else
{
this.ModelState.AddModelError("Username",
"The combination of username and password are incorrect!");
return View();
}
Redirect("~/");
}
As you can guess, this kind of code will create dependencies on the WebSecurity
class. We don't want to create these dependencies as the code becomes unverifiable. I'm a firm believer of Test-Driven Development, and I think these kind of dependencies should be broken to create some form of quality and testability in your code. Because the WebSecurity
class is a static helper we need to create a wrapper. This wrapper will implement an interface with similar or exactly the same operations as the WebSecurity
class. The wrapper class will then delegate its operations to WebSecurity
.
Forced Authorization and configuration
Now that we have our entities, our database and our testable code, we can now start adding features. But before we can do this, we need to setup our database connection for WebSecurity, and we need to find a way to only allow users with credentials into our system. These are two, very easy tasks.
Configuring the database
To start using WebSecurity, we need to tell WebSecurity
which database with the information about the user. We can do this by calling the InitializeDatabaseConnection
method. This method has a few parameters that need to passed with it. The first parameter is the name of the connection string. The second parameter is the table name which stores the our employees or 'UserProfiles'. The third parameter is the identifier for the user. The forth parameter is the username. and the last parameter is to tell the ASP.NET membership to go ahead and create the tables for us. Our initialization code looks like this. We will run this code by calling it in the Global.asax.
public static class AuthConfig
{
public static void RegisterAuth()
{
if (!WebSecurity.Initialized)
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"Employees", "UserId", "Name", true);
if (!WebSecurity.UserExists("Admin"))
{
var anonymous = new { Address = "Somewherestreet" };
WebSecurity.CreateUserAndAccount("Admin", "$ecret01", anonymous);
}
}
}
}
Force authorization
To force authentication by the user, we need to tell something specific to the ASP.NET application. We need to use the AuthorizationAttribute
on an action, a controller, or we can add the attribute to the global filters. Instead of marking each controller with an Authorization attribute, we are going to add the attribute to the global filters. This will allow us to deny all unauthorized user, and redirect them automatically to the login page. To achieve this, we need to add a few lines of configuration in our web.config file in the System.Web
namespace. And we need to add the AuthorizeAttribute
to the global filters in the FilterConfig
class.
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login/" timeout="2880" />
</authentication>
</system.web>
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
}
Conclusion
After you have completed the above tasks you can start working with the different features of WebSecurity. It just requires some initial setup. In the next article, we are going to look into basic operations of WebSecurity. Basic operations such as: logging in and out, adding a new employee with its properties and, changing and resetting a password.
Points of Interest
- Global filters allow you to add the
AuthorizationAttribute
to every request of the application.
History
- 16 June 2013 - Initial setup.
- 17 June 2013 - Finished.