Introduction
This is going to be a series of articles on ASP.NET Core Identity and each article will have a github repo with the complete code sample. In this first article, we shall investigate what is claim based authentication, how ASP.NET Core Identity implements it and overall how the identity system works.
ASP.NET Core Identity is the new membership system that comes with .NET core. Unlike the previous version it uses claims based authentication which is one of the biggest chnages from the previous versions of ASP.NET Identity.
Claims-based Authentication
Before we start exploring the Identity system, we will spend some time to understand claims-based authentication.
Let's consider a scenario where Bob who is a university student goes to a bank to open an account. Now the bank manager wants to see some kind of document that proves Bob's identity. Bob gives his driving license which has his Name, Date of Birth and Address.
Now Bob also wants to apply for the discount the bank offers only to students. The manager again asks for some document that proves that Bob is a student. This time Bob gives his student card which has his Name, University Name, Department Name and Student Number.
This real life scenario can be used as an analogy to explain claims-based authentication. Here Bob is the User who is using claims-based authentication. He is using two identities (a user can have multiple identities) - Driving License and Student Card. If identity has claims attached to it - Name, DOB, Student Number, etc.
Implementation in ASP.NET Core Identity
A good thing about ASP.NET Core is it is open source and if needed, we can investigate the source code to understand how it works. ASP.NET Core Identity source code is available in this github repo - https://github.com/aspnet/Identity
In the current implementation of ASP.NET Core, a User is of type ClaimsPrincipal
which implements IPrincipal
. ClaimsPrincipal
Class is implemented under System.Security.Claims
namespace.
public class ClaimsPrincipal : IPrincipal
{
...........
...........
public virtual IIdentity Identity { get; }
public virtual IEnumerable<ClaimsIdentity> Identities { get; }<claimsidentity><claim>
public virtual IEnumerable<Claim> Claims { get; }
...........
...........
public virtual bool HasClaim(Predicate<Claim> match);
<claim> public virtual bool HasClaim(string type, string value);
public virtual bool IsInRole(string role);
...........
...........
}
</claim></claim></claimsidentity>
From the implementation, we can see that the ClaimsPrincipal
class has Identities
property which returns the collection of ClaimsIdentity
. That means a User can have multiple identities.
There is another property Identity
. Don't let it confuse you, it returns the primary claims identity associated with this claims principal.
Another important property is Claims
which returns a collection that contains all of the claims from all of the claims identities associated with this claims principal.
Now let's look at the ClaimsIdentity
class.
public class ClaimsIdentity : IIdentity
{
...........
...........
public virtual string AuthenticationType { get; }
public virtual string Name { get; }
public virtual bool IsAuthenticated { get; }
public virtual IEnumerable<Claim> Claims { get; }
<claim> public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match);<claim><claim>
public virtual Claim FindFirst(string type);
public virtual bool HasClaim(string type, string value);
...........
...........
}
The AuthenticationType
returns the type which could be something like Basic, Windows, Cookie, etc.
The Claims
property returns the collection of claims associated with this identity.
So now if we try to map Bob's example with these classes, it will look like this:
Interacting with ASP.NET Core Identity
ASP.NET Core Identity has implemented some APIs (SignInManager
, UserManager
,RoleManager
, etc.) which simplifies the interactions with the identity objects. When working in an ASP.NET Core project dependency injection will provide the objects for these classes so that we can use those.
For example, SignInManager
implements the following public
method to sign in a user:
public virtual async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties,
string authenticationMethod = null)
{
var userPrincipal = await CreateUserPrincipalAsync(user);
if (authenticationMethod != null)
{
userPrincipal.Identities.First().AddClaim(new Claim
(ClaimTypes.AuthenticationMethod, authenticationMethod));
}
await Context.SignInAsync(IdentityConstants.ApplicationScheme,
userPrincipal,
authenticationProperties ?? new AuthenticationProperties());
}
If you want to learn how Login, Logout and other processes work, I encourage you to download and have a look at the source code.
In the next articles, I will start with an empty project and step by step add different functionalities like Login, Logout, Token generation, etc.