Integrating Identity Framework (Individual User Accounts)

When we select Individual User Accounts as Authentication Method while creating a new project (MVC Framework in this case) we have Identity Framework added to our solution with a basic default configuration.

Here is a list of the main components that are added:

  • AccountController: it handles the user’s registration, login and logout. It includes as well user’s validation and complementary workflows, such as the Reset Password workflow and the Forget Password workflow. Is a sample controller with the most common configuration that can be customized for our needing.
  • AccountViewModels/Account Views: view models and views for the AccountController.
  • ManageController: it handles the user management pages in our MVC Framework application. Is complementary with the AccountController. Is a sample controller with the most common configuration that can be customized for our needing.
  • ManageViewModels/Manage Views: view models and views for the ManageController.
  • IdentityModels: here we can find the Identity EF Context. It works as for a normal EF Code First application. The ApplicationUser class can be extended to add custom properties that we want to store for our users.

The IdentityConfig.cs and Startup.Auth.cs files are added as well under the App_Start folder. This files contain many classes that can be used to configure many behaviors, such as the email/SMS services to send email and SMS, the password strength requirements, the cookie authentication policy and so on.

Identity Framework uses OWIN (Open Web Interface for .Net) to decouple our application from the web server. This allow the application to work with different configurations of OS + WebServer, without being tied to Windows and IIS.


Setting the target DB for Identity Framework Data

As for any other EF Code first application we can do this by setting the connection string when we create the context:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base(@"Data Source=.\SQLEXPRESS;Initial Catalog=TestIdentityDB;Integrated Security=True", throwIfV1Schema: false)
    {
    }
 
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

At the first attempt to access the DB this will be created (as for any normal EF Code First application). Here is its general structure:

The tables are all empty. If we try to register a new user a record will be created into the AspNetUsers table. Here is how it looks like:


Setting an action to require authentication

We can use the Authorize attribute to specify an action that requires authentication. An user that isn’t logged in will be sent to the login page if he tries to access the action.

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

From the view is always possible to access the current user’s information through the User.Identity object:

@using Microsoft.AspNet.Identity
 
@{
    ViewBag.Title = "AuthenticatedPage";
}
 
<h2>AuthenticatedPage</h2>
<h3>Welcome @User.Identity.GetUserName()</h3>

Setting an action to not require authentication

The Authorize attribute can be set also at controller-level. This means that all the controller’s actions will inherit the attribute without needing a specific configuration. In this scenario where the authentication is required as default behavior we can use the AllowAnonymous attribute to specify that an action shouldn’t require authentication.

[Authorize]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
 
    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";
 
        return View();
    }
 
    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";
 
        return View();
    }
 
    [AllowAnonymous]
    public ActionResult AuthenticatedPage()
    {
        return View();
    }
}

Setting an action to require role-based authentication

In addition to just requiring our user to be somehow authenticated in order to access a certain action, we can also require that the user is part of some authorization group (administrators for instance). In order to do this we need, first of all, to have users associated with roles into our Identity database. This can be easily done through a custom EF Initializer that will seed our Code First database with some basic user and role:

public class IdentityInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
    protected override void Seed(ApplicationDbContext context)
    {
        var passwordHasher = new PasswordHasher();
 
        var adminRole = new IdentityRole("Admin");
        var testRole = new IdentityRole("Test");
 
        var adminUser = new ApplicationUser
        {
            UserName = "admin@admin.com",
            Email = "admin@admin.com",
            PasswordHash = passwordHasher.HashPassword("admin"),
            SecurityStamp = Guid.NewGuid().ToString()
        };
        adminUser.Roles.Add(new IdentityUserRole
        {
            UserId = adminUser.Id,
            RoleId = adminRole.Id
        });
 
        var testUser = new ApplicationUser
        {
            UserName = "test@test.com",
            Email = "test@test.com",
            PasswordHash = passwordHasher.HashPassword("test"),
            SecurityStamp = Guid.NewGuid().ToString()
        };
        testUser.Roles.Add(new IdentityUserRole
        {
            UserId = testUser.Id,
            RoleId = testRole.Id
        });
 
        //Creates Roles
        context.Roles.Add(adminRole);
        context.Roles.Add(testRole);
 
        //Creates Users
        context.Users.Add(adminUser);
        context.Users.Add(testUser);
    }
}

Here is what the code above will insert into the database:

Now that our users are linked to roles we can specify, for instance, an action that will be accessible only for users in the “Admin” group. An user that isn’t part of that group will be sent to the login page if he tries to access the action. We can do this in a very easy way by passing some argument to the Authorize attribute.

[Authorize(Roles = "Admin")]
public ActionResult AuthenticatedPage()
{
    return View();
}
Advertisements