Author : MD TAREQ HASSAN | Updated : 2021/07/11
Ways To Authenticate
- ASP.Net Core Identity (IdentityBbContext, IdentityUser, EF Core Code First Migration etc.)
- OpenID Connect
- Microsoft Identity Platform (Microsoft Identity Web)
- Okta
- etc.
- IdentityServer4 (Open source OpenID Connect provider)
- Platform Level Authentication
- Azure App Service Easy Auth
- Azure AKS Easy Auth
- Azure API Management Authentication
Things You Should Know
- ASP.Net core Identity
- What is Json Web Token (JWT)
- What is OAuth 2.0
- What is OpenID Connect?
- OpenID Connect Providers
Microsoft Identity Platform
- New version of “Azure AD for developers”
- Unified platform to implement OpenID Connect using Azure AD
Microsoft Identity Web
- Implementation of Microsoft Identity Platform for ASP.Net
- In other words, implementation of OpenID Connect using Azure AD for ASP.Net
Asp.Net Core Identity
- Authentication and Authoriztion Framework that comes with .Net Framework
- Uses Entity Framework Core Code First migration
- Also provides UIs for managing user information and login functionality
See: Scaffolding default implementation or 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
- Visual Studio templates creates
ApplicationUser
is which is an implementation ofIdentityUser
- It’s meant to simplify the extension of the user object
- it doesn’t actually add anything
- because it hides all of the generic properties, it can cause some confusion when using other parts of the library
- It is perfectly acceptable to use IdentityUser directly
- Links:
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
- Roles is the only real difference between the two
AddDefaultIdentity
: removes roles, use when you do not need rolesAddIdentity
: use when you need roles
- AddDefaultIdentity was introduced in ASP.NET Core 2.1. Calling AddDefaultIdentity is similar to calling the following:
- AddIdentity
- AddDefaultUI
- AddDefaultTokenProviders
- See: AddDefaultIdentity<TUser> implementation code at Github
- To add role when using AddDefaultIdentity:
services.AddDefaultIdentity<ApplicationUser>().AddRoles<ApplicationRole>()
- Links:
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 byUserManager<TKey>
UserManager<TKey>
is used bySignInManager<TKey>
- Custom property
CustomTag
added to IdentityUser, so we need to create migration and update database (EF Core will create table column forCustomTag
) - 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