How to Customise ASP.NET Core Identity

In this article, we are going to learn how to Customize ASP.NET CORE identity in simple steps.

Creating ASP.NET Core Application

We are going to create ASP.NET Core Web Application for that we are going to choose ASP.NET Core Web Application template.

After choosing template Next, we are going to enter Project Name DemoWebIdentityCustom in Project Name box, and along with that we can configure Location of project and Solution name and Then, click on Create button.

After clicking on create button a new dialog appear here, we are going to choose .Net Core framework and ASP.NET Core Version 3.0 as the framework for the application, and in advance settings, we are not going to enable docker settings for this project.

For working with ASP.NET Core Identity, we are going to change the Authentication of project.

Changing Authentication
When you click on change link a new dialog popup which show various Authentication methods.

  1. No Authentication
  2. Individual User Accounts (Authenticate User in membership database)
  3. Work or School Accounts (Authenticate User using Azure Active Directory)
  4. Windows Authentication (Authenticate User using intranet environment)

We are going to select Individual User Accounts and in dropdown select store user account in-app and then click OK button.

Finally, click on Create button to Create project.
Project structure
The project structure generated according to the configuration.

Default Identity
After creating the project, we have default identity added to the project you can see it in Data folder –> migration files and in Area folder –> Identity folder.

Configure Connection String

In the next part, we are going to Configure connection string in appsettings.json file. The default connection string which is available connects to SQL Server Express LocalDB. If you are using SQL Server Express, then you do not require to change connection.
Note: – Microsoft SQL Server Express LocalDB is a feature of SQL Server Express targeted to developers. It is available on SQL Server Express with Advanced Services.

Default Connection String

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-DemoWebIdentity-07FF5D8C-C870-433D-BA82-D197481A6D92;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

If you want to connect to LocalDB from SQL Server Management Studio, then enter (localdb)\MSSQLLocalDB as Server Name and Select Authentication Type as Windows Authentication.

For this project, we are going to use SQL Server Authentication for doing that we are going to make a change in the Connection string, as shown below.

Connection String

{
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=DESKTOP-DUM6D15; initial catalog=DemoIdentity; user id=sa; password=####;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

After Setting Connection String next, we are going to add Adding a class ApplicationUser.

Adding Class ApplicationUser
We are going to add a class with the name ApplicationUser, and it inherits the IdentityUser class. In this class, we are going to add additional properties which we need. In default ASP.NET Core identity registration model has only 3 fields which are email id, password and confirm password. Here we are adding 3 additional fields ContactNo, State, City.
We are going to add ApplicationUser class in the Models folder.

using Microsoft.AspNetCore.Identity;

namespace DemoWebIdentityCustom.Models
{
    public class ApplicationUser : IdentityUser
    {
        public string ContactNo { get; set; }
        public string State { get; set; }
        public string City { get; set; }
    }
}

After adding class next, we are going to add Identity UI to Project using Scaffolding. For customising.

Adding Identity UI to Project using Scaffolding
For adding Identity UI from Solution Explorer, right-click on the project -> Add -> New Scaffolded Item.

After clicking on New Scaffolded item, a new dialog pop up as shown below.

After selecting Identity select on Add button to Add identity.

A new dialog Add identity pop up with Name Add identity in this dialog we can see multiple files related to the identity we can select single or multiple files to override. We are going to override all files for this demo for that we are going to select all files. And in data context class we are going to select ApplicationDbContext class and click on Add button.

Once we have completed with adding Identity, you see all code related to files; we chosen files are been added to Accounts folder in Identity directory.

After adding files next, we are going to register AddIdentityCore service for that we are going to add.

Registering AddIdentityCore Service

We are going to register AddIdentity service as shown below. If you see, we have removed IdentityUser and registered custom class ApplicationUser.

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

Code Snippet

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

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

    services.AddControllersWithViews();
    services.AddRazorPages();
}

After registering service next, we are going to make a change in ApplicationDbContext class.

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

namespace DemoWebIdentityCustom.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}

Here we are going to inherit from IdentityDbContext<TUser> TUser is a type of user objects.

We are going to pass custom class ApplicationUser to IdentityDbContext.

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

namespace DemoWebIdentityCustom.Data
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}

After completing with making a change in ApplicationDbContext class next, we are going to create a new migration.

Creating New Migration
For adding a migration, we are going to run command add-migration, and we are going to run this command in the package manager console.

Add-Migration FirstMigration

After creating migration next, we are going to Run update-database command to apply the migration to the database.

Applying this migration to the database
For doing that we are going to run command Update-Database from the Package Manager Console.

Update-Database command to create a database and schema which is there in the main migrations file.
Now opens your SQL Management studio to check created database.

Database Created after applying Migration

Now we have a database and scheme ready next we are going to add a property to the state, city, contactno to InputModel class which is inside RegisterModel class.

Adding New Properties to InputModel class

public class InputModel
 {
     [Required]
     [EmailAddress]
     [Display(Name = "Email")]
     public string Email { get; set; }

     [Required]
     [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
     [DataType(DataType.Password)]
     [Display(Name = "Password")]
     public string Password { get; set; }

     [DataType(DataType.Password)]
     [Display(Name = "Confirm password")]
     [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
     public string ConfirmPassword { get; set; }

     [Display(Name = "State")]
     public string State { get; set; }

     [Display(Name = "City")]
     public string City { get; set; }

     [Display(Name = "ContactNo")]
     public string ContactNo { get; set; }
 }

Next, we are going to add this Property on Register View which is present in Areas -> Identity -> Pages -> Account folder.

Adding New Fields to View

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.City"></label>
                <input asp-for="Input.City" class="form-control" />
                <span asp-validation-for="Input.City" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.State"></label>
                <input asp-for="Input.State" class="form-control" />
                <span asp-validation-for="Input.State" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ContactNo"></label>
                <input asp-for="Input.ContactNo" class="form-control" />
                <span asp-validation-for="Input.ContactNo" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h4>Use another service to register.</h4>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. 
See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                            for details on setting up this ASP.NET application to support logging in via external services.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

After adding Newly added properties to View next, we are going Make changes in OnPostAsync Method of RegisterModel class.
Here we are going to change IdentityUser class to ApplicationUser class.

Old Code Snippet

var user = new IdentityUser
 {
     UserName = Input.Email,
     Email = Input.Email
 };

New Code Snippet of OnPostAsync Method

var user = new ApplicationUser 
{ 
    UserName = Input.Email, 
    Email = Input.Email ,
    State = Input.State,
    City = Input.City,
    ContactNo =Input.ContactNo
};

Below code snippet after adding New Model in OnPostAsync Method.

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            if (ModelState.IsValid)
            {

                var user = new ApplicationUser 
                { 
                    UserName = Input.Email, 
                    Email = Input.Email ,
                    State = Input.State,
                    City = Input.City,
                    ContactNo =Input.ContactNo
                };


                var result = await _userManager.CreateAsync(user, Input.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                    var callbackUrl = Url.Page(
                        "/Account/ConfirmEmail",
                        pageHandler: null,
                        values: new { area = "Identity", userId = user.Id, code = code },
                        protocol: Request.Scheme);

                    await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    if (_userManager.Options.SignIn.RequireConfirmedAccount)
                    {
                        return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
                    }
                    else
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        return LocalRedirect(returnUrl);
                    }
                }
                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }

Finally, a change which we are going to make is to Replace IdentityUser to ApplicationUser in RegisterModel Class as shown below.

Replacing IdentityUser to ApplicationUser

Now we have completed with all configuration let’s run application and test it.
OH, we got an error while running Application.
An unhandled exception occurred while processing the request.

InvalidOperationException: No service for type ‘Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]’ has been registered.

To fix this error, we need to make a change in application View by replacing IdentityUser with Custom class ApplicationUser, as shown below.

On other Views, if we encounter this kind of error, then just replace IdentityUser with Custom class ApplicationUser.

Now we are completed will configuration and error resolving let’s run application and register User.

Registering New User

Table View after registering User

Output

Now we have Learned how to customizing ASP.NET Core Identity in step by step.

By Saineshwar Bageri

I am Microsoft MVP | C# Corner MVP | Code Project MVP | FULL STACK .NET Developer and working on .Net Web Technology (Asp.net, Asp.net Core,.Net Core, C#, Sqlserver, MVC, Windows, Console Application, javascript, jquery, json, ORM Dapper) and also a freelance developer.