How to Customize Asp.NET Identity Core with External Database Storage step by step

ASP.NET Core Identity is designed to enable us to easily use a number of different storage providers for our ASP.NET applications. We can use the supplied Identity providers that are included with the .NET Framework, or we can implement your own providers.

There are two primary reasons for creating a custom Identity  provider.

We need to store Identity information in a data source that is not supported by the Identity providers included with the .NET Framework, such as a MysQL database, an Oracle database, or other data sources.
We need to manage Identity information using a database schema that is different from the database schema used by the providers that ship with the .NET Framework.

A common example of this would be to use authentication data that already exists in a SQL Server database for a company or Web site.
In this tutorial, we are going to implement and configure a custom Identity Provider using ASP.NET MVC Core and IndentityCore

Create Asp.NET Core   with individual user account Project

STARTUP

Open Package Manager Console and run Update-Database.

UpdateDatabase

The migration schema in folder Data/Migrations will be applied to create the database in localDB.

The connectionString is in file appSettings.json

ConnectionString

 

Migration

Open Server explorer to view generated database tables :  AspNetUsers, AspNetRoles, AspNetRoleClaims, AspNetUserClaims, AspNetUserLogins, AspNetUserRoles,AspNetUserTokens

Database

Schema

Lets press F5 to run our projet and click on << Register >> link so as to create a new account as follow

Lets enter our email and our password and click register button

register

A new record is added to aspNetUsers table

TableData

Our application is now ready to use an external storage, on the next section, we will show you how to customize the external database storage

Create Identity Library Project

Lets create a Class Library project and name it IdentityCore.Library

Right click reference and select Manage Nuget Packages

Untitled

Install package Microsoft.AspNet.Identity.Core  and Microsoft.AspNetCore.Identity.EntityFrameworkCore

Nuget

Create an MyIdentityUser class to hold user informations, it inherits from IdentityUser  and an  MyIdentityRole  class that inherits from IdentityRole<string>

public class MyIdentityUser : IdentityUser
    {
        public DateTime? JoinDate { get; set; }
        public string JobTitle { get; set; }
        public string Contract { get; set; }

        public MyIdentityUser()
        {
            Id = Guid.NewGuid().ToString();
        }
    }

MyIdentityRole class hold user roles informations

 public class MyIdentityRole<T> : IdentityRole<string>
    {
        public MyIdentityRole()
        {
            Id = Guid.NewGuid().ToString();
        }

        public MyIdentityRole(string name) : this()
        {
            Name = name;
        }

        public MyIdentityRole(string name, string id)
        {
            Name = name;
            Id = id;
        }

        public string Description { get; internal set; }
    }

we will implement MyUserStore to  customize our storage provider

 public class MyUserStore<T> : IUserStore<T> where T : MyIdentityUser
    {
        public Task<IdentityResult> CreateAsync(T user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IdentityResult> DeleteAsync(T user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public Task<T> FindByIdAsync(string userId, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<T> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetNormalizedUserNameAsync(T user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetUserIdAsync(T user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetUserNameAsync(T user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetNormalizedUserNameAsync(T user, string normalizedName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetUserNameAsync(T user, string userName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IdentityResult> UpdateAsync(T user, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }

Lets Create a MyRoleStore Class and implement IRoleStore

public class MyRoleStore<T> : IRoleStore<T> where T : MyIdentityRole<string>
    {
        public Task<IdentityResult> CreateAsync(T role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IdentityResult> DeleteAsync(T role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        public Task<T> FindByIdAsync(string roleId, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<T> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetNormalizedRoleNameAsync(T role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetRoleIdAsync(T role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<string> GetRoleNameAsync(T role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetNormalizedRoleNameAsync(T role, string normalizedName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task SetRoleNameAsync(T role, string roleName, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public Task<IdentityResult> UpdateAsync(T role, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }

Configure Asp.Net client to target our custom Identity Library

Open Startup.cs file and remove the following code :

services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

Paste the following code : insteadof  using ApplicationUser and  IdentityRole, we are going to use : MyIdentityUser and MyIdentityRole

services.AddIdentity<MyIdentityUser, MyIdentityRole<string>>(
                 config =>
                 {
                     config.SignIn.RequireConfirmedEmail = true;
                 })
                 .AddDefaultTokenProviders();

services.AddTransient<IUserStore<MyIdentityUser>, MyUserStore<MyIdentityUser>>();
services.AddTransient<IRoleStore<MyIdentityRole<string>>, MyRoleStore<MyIdentityRole<string>>>();

And finally replace all usage of  ApplicationUser  by  MyIdentityUser

 [Authorize]
    [Route("[controller]/[action]")]
    public class AccountController : Controller
    {
        private readonly UserManager<MyIdentityUser> _userManager;
        private readonly SignInManager<MyIdentityUser> _signInManager;
        private readonly IEmailSender _emailSender;
        private readonly ILogger _logger;

        public AccountController(
            UserManager<MyIdentityUser> userManager,
            SignInManager<MyIdentityUser> signInManager,
            IEmailSender emailSender,
            ILogger<AccountController> logger)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _emailSender = emailSender;
            _logger = logger;
        }
}

Press F5  to run application,  and  register a new account

Thats all , we are using our new framwork to manage our custom storage logic

 Exception

Download source code

Best regards.

Gora LEYE

I'm a microsoft most valuable professional (MVP) .NET Architect and Technical Expert skills located in Paris (FRANCE). The purpose of this blog is mainly to post general .NET tips and tricks, www.masterconduite.com Gora LEYE

Support us

BMC logoBuy me a coffee