Author : HASSAN MD TAREQ | Updated : 2020/05/28

Prerequisite

See: ASP.Net core Identity

Configurations in Startup

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    // services for Identity
    services.AddDbContextPool<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
	
    // Identity for app
    services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

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

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	// ... ... ...

	app.UseRouting();

	app.UseAuthentication();
	app.UseAuthorization();

	app.UseEndpoints(endpoints =>
	{

		endpoints.MapControllerRoute(
			name: "default",
			pattern: "{controller=Home}/{action=Index}/{id?}"
		);

		endpoints.MapRazorPages();
	});
}

Extending default identity implementation

We are gonna extend default implementation and add properties according to our need.

First of all: Scaffold default identity implementation

ApplicationUser.cs

//
// extending default implemention of IdentityUser<TKey>
// class IdentityUser: IdentityUser<string> { }
//
public class ApplicationUser : IdentityUser
{
    public string CustomTag { get; set; }
}

ApplicationDbContext.cs

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
    {
	
    }
}

AddDefaultIdentity vs AddIdentity

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
	services.AddDbContextPool<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
	
	services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
	    .AddEntityFrameworkStores<ApplicationDbContext>();

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

Replace IdentityUser with ApplicationUser in Views/Shared/_LoginPartial.cshtml

@using Microsoft.AspNetCore.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

Notes:

  • IUserStore<TKey> is used by UserManager<TKey>
  • UserManager<TKey> is used by SignInManager<TKey>
  • Custom property CustomTag added to IdentityUser, so we need to create migration and update database (EF Core will create table column for CustomTag)
  • PMC:
    • Add-Migration AddCustomTag
    • Update-Database

Change the primary key type of ApplicationUser

A change to the PK column’s data type after the database has been created is problematic on many database systems. Changing the PK typically involves dropping and re-creating the table. Therefore, key types should be specified in the initial migration when the database is created.

ApplicationUser.cs

//
// extending IdentityUser<TKey>
// TKey => Guid
//
public class ApplicationUser : IdentityUser<Guid>
{
    public string CustomTag { get; set; }
}

ApplicationRole.cs

public class ApplicationRole : IdentityRole<Guid>
{
    public string Description { get; set; }
}

ApplicationDbContext.cs

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

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options): base(options)
    {
	
    }
}

We need to use AddIdentity instead of AddDefaultIdentity (because AddDefaultIdentity does have role).
Also should call .AddDefaultUI() & .AddDefaultTokenProviders()

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
	services.AddDbContextPool<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
	
	services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.SignIn.RequireConfirmedAccount = true)
	    .AddEntityFrameworkStores<ApplicationDbContext>()
		.AddDefaultUI()
		.AddDefaultTokenProviders();

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

See: Add navigation properties

Custom Implementation

// Uses all the built-in Identity types
// Uses `string` as the key type
public class IdentityDbContext: IdentityDbContext<IdentityUser, IdentityRole, string>
{
}

// Uses the built-in Identity types except with a custom User type
// Uses `string` as the key type
public class IdentityDbContext<TUser>: IdentityDbContext<TUser, IdentityRole, string> where TUser : IdentityUser
{
}

// Uses the built-in Identity types except with custom User and Role types
// The key type is defined by TKey
public class IdentityDbContext<TUser, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>, IdentityUserLogin<TKey>, IdentityRoleClaim<TKey>, IdentityUserToken<TKey>>
        where TUser : IdentityUser<TKey>
        where TRole : IdentityRole<TKey>
        where TKey : IEquatable<TKey>
{
}

// No built-in Identity types are used; all are specified by generic arguments
// The key type is defined by TKey
public abstract class IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>: IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>
         where TUser : IdentityUser<TKey>
         where TRole : IdentityRole<TKey>
         where TKey : IEquatable<TKey>
         where TUserClaim : IdentityUserClaim<TKey>
         where TUserRole : IdentityUserRole<TKey>
         where TUserLogin : IdentityUserLogin<TKey>
         where TRoleClaim : IdentityRoleClaim<TKey>
         where TUserToken : IdentityUserToken<TKey>
{
}

You can extend any Entity/IdentityModel and use it in application uisng overloads of IdentityDbContext (ApplicationDbContext)

services.AddDbContextPool<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
	.AddEntityFrameworkStores<ApplicationDbContext>();

Example: Custom storage providers for ASP.NET Core Identity

Azure App Service Easy Auth

See: Azure app service easy auth in Blazor