Installing AspNetCore.Identity

In order to set up and configure the AspNetCore.Identity framework, we need to install the required NuGet package and perform a number of code changes in some of our project's entity classes.

Adding the package

The first thing we're going to do is to check for the existence of the Microsoft.AspNetCore.Identity.EntityFrameworkCorelibrary package, which we should have already added in Chapter 4, The Data Model. If we missed it, we can fix the issue in a number of ways.

If we like to use the Package Manager Console, we can select the appropriate tab and write the following command:

> 
Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore

Adding the package

If we prefer the Package Manager GUI interface, right-click in Solution Explorer to the OpenGameListWebApp project node, select Manage NuGet Packages, and act accordingly:

Adding the package

As usual, we can also manage everything directly from the project.json file by adding the following line to the dependencies section:

    "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0" 

Updating the project classes

Once done, we need to perform some changes to our project's classes to ensure a proper Identity support.

ApplicationDbContext.cs

Open the Data/ApplicationDbContext.cs class file and perform the following changes:

  1. Add a using reference to Microsoft.AspNetCore.Identity.EntityFrameworkCore, as required by the new base class:
            using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 
    
  2. Change the base class from DbContext to IdentityDbContext<ApplicationUser>:
            public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
    
  3. Remove the DbSet<ApplicationUser> Users property, as the IdentityDbContext base class already has it built in:
            #region Properties 
            public DbSet<Item> Items { get; set; } 
            public DbSet<Comment> Comments { get; set; } 
            // public DbSet<ApplicationUser> Users { get; set; } 
            #endregion Properties 
    

ApplicationUser.cs

If we try to compile the project, this file will now produce an error, because our existing ApplicationUser class does not extend the IdentityUser type, which is a requirement for the TUser, generic type required by the IdentityDbContext class. To solve the error, switch to the /ApplicationUsers/ApplicationUser.cs class and add the IdentityUser base class in the following way:

namespace OpenGameListWebApp.Data.ApplicationUsers 
{ 
    public class ApplicationUser : IdentityUser 
    { 

Needless to say, we'll have to add a reference to the AspNetCore.Identity namespace here as well:

using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 

As soon as we save the file, we'll get three green compilation notices for the Id, Email, and UserName properties, as they are all already present in the IdentityUser base class:

ApplicationUser.cs

We don't need them anymore, so we can comment (or just remove) them as well:

//[Key] 
//[Required] 
//public string Id { get; set; } 
//[Required] 
//[MaxLength(128)] 
//public string UserName { get; set; } 
//[Required] 
//public string Email { get; set; } 

That's it! From now on, our ApplicationUser entity class is also an IdentityUser that can be used by ASP.NET Identity for authentication and authorization purposes.

Startup.cs

What we need to do now is to add the Identity-related services to our project's startup class. Open the Startup.cs file and add the following to the ConfigureServices method, right before the DbContext (new lines are highlighted):

public void ConfigureServices(IServiceCollection services) 
{ 
    // Add framework services. 
    services.AddMvc(); 
 
    // Add EntityFramework's Identity support. 
    services.AddEntityFramework(); 
 
    // Add Identity Services & Stores

    services.AddIdentity<ApplicationUser, IdentityRole>(config => {

        config.User.RequireUniqueEmail = true;

        config.Password.RequireNonAlphanumeric = false;

        config.Cookies.ApplicationCookie.AutomaticChallenge = false;

    })

        .AddEntityFrameworkStores<ApplicationDbContext>()

        .AddDefaultTokenProviders(); 
 
    // Add ApplicationDbContext. 
    services.AddDbContext<ApplicationDbContext>(options => 
        options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]) 
        ); 
 
    // Add ApplicationDbContext's DbSeeder 
    services.AddSingleton<DbSeeder>(); 
} 

In order to make it work, we also need to add the following namespaces:

using OpenGameListWebApp.Data.Users; 
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 

DbSeeder.cs

Since we changed our ApplicationUser class to make it extend the IdentityUser base class, we most likely broke the seeding mechanism we set up back in Chapter 4The Data Model. On top of that, we should also create some sample roles, since we now we can make good use of them. These are two good reasons to revise our current DbSeeder class.

Let's open our /Data/DbSeeder.cs file and update it accordingly. This is a fat class in terms of source code lines, so we'll just show the relevant changes.

The first thing we need to do is to add a UserManager and a RoleManager, as they are the required Asp.NetCore.Identity handler classes to properly work with users and roles. We can define a private variable for each one of them within the #Private Members region (new lines are highlighted):

#region Private Members 
private ApplicationDbContext DbContext; 
private RoleManager<IdentityRole> RoleManager;

private UserManager<ApplicationUser> UserManager; 
#endregion Private Members 

These references will require the following namespaces:

using Microsoft.AspNetCore.Identity; 
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 

We can then instantiate these new properties within the Constructor using the same dependency injection pattern we already used to instantiate our ApplicationDbContext:

#region Constructor 
public DbSeeder(ApplicationDbContext dbContext, RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager) 
  { 
      DbContext = dbContext; 
      RoleManager = roleManager;

      UserManager = userManager; 
  } 
#endregion Constructor 

Right after that, we need to change our CreateUsers method to make use of these handlers. Since they all feature methods enforcing async/await programming pattern, we also need to make it async and change its return type from void to Task. Therefore, we will also conveniently rename it CreateUsersAsync as well. Here's the new method, rewritten from scratch:

private async Task CreateUsersAsync()
{
    // local variables
    DateTime createdDate = new DateTime(2016, 03, 01, 12, 30, 00);
    DateTime lastModifiedDate = DateTime.Now;
    string role_Administrators = "Administrators";
    string role_Registered = "Registered";

    //Create Roles (if they doesn't exist yet)
    if (!await RoleManager.RoleExistsAsync(role_Administrators)) await RoleManager.CreateAsync(new IdentityRole(role_Administrators));
    if (!await RoleManager.RoleExistsAsync(role_Registered)) await RoleManager.CreateAsync(new IdentityRole(role_Registered));

    // Create the "Admin" ApplicationUser account (if it doesn't exist already)
    var user_Admin = new ApplicationUser() {
        UserName = "Admin",
        Email = "[email protected]",
        CreatedDate = createdDate,
        LastModifiedDate = lastModifiedDate
    };

    // Insert "Admin" into the Database and also assign the "Administrator" role to him.
    if (await UserManager.FindByIdAsync(user_Admin.Id) == null)
    {
        await UserManager.CreateAsync(user_Admin, "Pass4Admin");
        await UserManager.AddToRoleAsync(user_Admin, role_Administrators);
        // Remove Lockout and E-Mail confirmation.
        user_Admin.EmailConfirmed = true;
        user_Admin.LockoutEnabled = false;
    }

#if DEBUG
    // Create some sample registered user accounts (if they don't exist already)
    var user_Ryan = new ApplicationUser() {
        UserName = "Ryan",
        Email = "[email protected]",
        CreatedDate = createdDate,
        LastModifiedDate = lastModifiedDate,
        EmailConfirmed = true,
        LockoutEnabled = false
    };
    var user_Solice = new ApplicationUser() {
        UserName = "Solice",
        Email = "[email protected]",
        CreatedDate = createdDate,
        LastModifiedDate = lastModifiedDate,
        EmailConfirmed = true,
        LockoutEnabled = false
    };
    var user_Vodan = new ApplicationUser() {
        UserName = "Vodan",
        Email = "[email protected]",
        CreatedDate = createdDate,
        LastModifiedDate = lastModifiedDate,
        EmailConfirmed = true,
        LockoutEnabled = false
    };
    // Insert sample registered users into the Database and also assign the "Registered" role to him.
    if (await UserManager.FindByIdAsync(user_Ryan.Id) == null)
    {
        await UserManager.CreateAsync(user_Ryan, "Pass4Ryan");
        await UserManager.AddToRoleAsync(user_Ryan, role_Registered);
        // Remove Lockout and E-Mail confirmation.
        user_Ryan.EmailConfirmed = true;
        user_Ryan.LockoutEnabled = false;
    }
    if (await UserManager.FindByIdAsync(user_Solice.Id) == null)
    {
        await UserManager.CreateAsync(user_Solice, "Pass4Solice");
        await UserManager.AddToRoleAsync(user_Solice, role_Registered);
        // Remove Lockout and E-Mail confirmation.
        user_Solice.EmailConfirmed = true;
        user_Solice.LockoutEnabled = false;
    }
    if (await UserManager.FindByIdAsync(user_Vodan.Id) == null)
    {
        await UserManager.CreateAsync(user_Vodan, "Pass4Vodan");
        await UserManager.AddToRoleAsync(user_Vodan, role_Registered);
        // Remove Lockout and E-Mail confirmation.
        user_Vodan.EmailConfirmed = true;
        user_Vodan.LockoutEnabled = false;
    }
#endif
    await DbContext.SaveChangesAsync();
} 

As we can see, we made some relevant changes here:

  • The DbContext.Add and DbContext.AddRange methods have been replaced by those provided by the UserManager. This allow us to specify a password that will be automatically hashed and also to avoid any explicit Id assignment, as they will be auto-generated.
  • We used RoleManager to create two sample roles: administrators and registered.
  • We modified our code to add the admin user to the administrators role and all the other sample users to the registered role.

Once done, we need to update the SeedAsync method to reflect the rename we just did on CreateUsersAsync and also handle the fact that the latter is now asynchronous as well:

#region Public Methods
public async Task SeedAsync()
{
    // Create default Users
    if (await DbContext.Users.CountAsync() == 0) await CreateUsersAsync();
    // Create default Items (if there are none) and Comments
    if (await DbContext.Items.CountAsync() == 0) CreateItems();
}
#endregion Public Methods

With this, we're done updating our project's classes.

Before going further, it might be wise to issue a whole project rebuild to make sure we're not getting build errors within our code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset