JWT Authentication in ASP .Net Core with an Identity Provider - Part II

.Net Core Jan 10, 2020

This is the second part of our series about jwt authentication in asp.net core, in the previous article I talk about JSON Web Token(JWT) structure in detail, also we discussed jwt authentication flow. In this article we implement jwt authentication flow in .net core with implementing an Identity Provider for issuing token, authentication and authorization and we will setup a Resources Server for hosting our API Resources and we will use our AuthServer for these API Resources.

The source code of this article is available in this repository.

Implementing JWT Authentication with Using .Net Core Identity

In this sample, I use a separate Identity Provider(IDP) or AuthServer as Identity and Authorization server and a Resources Server for Hosting Our API Resources. On the Resources Server, we have multi API Resources that protected for accessing anonymous users and for accessing these APIs we need to authorize ourselves to Identity server APIs with an access token from Identity Provider. On the other server we have a unified Identity Provider that uses to protect, Authenticate, Authorize, Issue a token and Identity management of our API Resources. I use a separate Identity Provider because we can use this Identity Server for all of our other related API resources in the future as a single unified service. we will have a single Identity Provider for authentication/authorization and issuing a token and with this approach, any change in other API resources doesn’t impact our Auth Server and it remains completely unchanged and stable.

Infrastructure for Authentication and Authorization with JSON Web Token (JWT)

For this article, I created some infrastructure classes for different purposes such as Authentication, Exception, Validation,…

Here I will explain some of these helper classes for authentication purposes.

The first class is JwtOptions that uses to configure our JwtMiddleware and JwtService.

This class gets its data from appsettings.json and JwtOptions section. we setup this Config class in the startup for dependency injection that other classes can inject JwtOptions and use its values. I put the full needed startup configuration code further in this article.

Another needed config class is AuthenticationSettings that use for specifies the type of signing key for our tokens such as Hmac or RSA. this class also will be configured in the startup for dependency injection for injecting this class in other classes.

Our priority is using RSA mechanism because in Hmac each party with having a secret key can issue a token and this is not good practice because we don’t want this in the parties, they only should validate the tokens not issuing a token, only AuthServer should issue a token. so for this purpose, we can use RSA mechanism for signing token and parties only will have a public key that only could use for validating token not issue a token and for issue a token we need a private key that only available on AuthServer.

Another important class is JwtService and it uses for validating and issuing a jwt token.

For config our jwt services and middleware, I create an extension method AddJwtAuthentication for registering required services and middleware for jwt authentication mechanism to simplicity but you can use it as you like, you should add this extension method to your service collection in the startup. this is our extension method for registering jwt authentication

In this method with GetCredentialSigningKey method we setup suitable signing key for our token with using configuration setting for RSA or Hmac key in appsettings.json file.

We can use this it both AuthServer and Resources Server but with different configuration in appsettings.json, for example for configuring it for AuthServer because we need issue a token so in AuthenticationSection of our appsettings.json file we should setup our private key but if you use this AddJwtAuthentication on Resources Server APIs in appsettings.json file, you should only setup a public key because the parties shouldn’t issue a token. we also should add Authentication middleware with UseAuthentication method to our Configure method. in .Net core 3 Authentication and Authorization separate in two different methods UseAuthentication and UseAuthorization.

I put two sample config files, for AuthServer and Resources Server here, for a bit more makes sense.

AuthServer JWT Authentication Configs for Issuing and validating token:

If there are both Hmac and RSA config settings in the config file, RSA will be select. you can remove each Hmac or RSA section if you don’t use it in your application, I put them here for a sample because you can see both of them in one place.

Resources Server JWT Authentication Configs for validating token for access to protected resources:

I don’t go more depth into infrastructure code because that is not in the scope of this article, you can see this article source code in this repository.

This article sample contains three parts:

  1. Autherver: use for authentication, authorization and identity management with using .Net Core Identity
  2. Resources Server: this part contains API Services that protect from public access by using AuthServer for authorizing access to resources.
  3. Client: this part contains a client application that uses protected API resources on Resources Server like an angular application, MVC application or a PostMan client. for simplicity in this article, I use PostMan as a client but you can use any other clients.

Configure AuthServer

Now let’s create our Identity Server as Identity and token management server, let’s call it AuthServer. I use dotnet CLI in VS Code to create our sample but you can use visual studio or rider if you like. First I create a directory for our sample with the name of Jwt Authentication and in within this folder, I create a solution for our sample with this command  dotnet new sln -n "Jwt Authentication", now I create our AuthServer with this command dotnet new webapi -n AuthServer and in the root of our sample add this project to our solution with this command  dotnet sln "Jwt Authentication.sln" add AuthServer/AuthServer.csproj. In this project, I want to use .Net Core Identity, so I add required Nuget packages for .Net Core Identity and EntityFramework to our identity project, so we go to this project folder and use the following commands to add needed packages to project

And for required packages for token management with JWT, we use these NuGet packages and add them to our project

And for setup our connection string for EntityFramework, we add following connection string to our appsettings.json file

Now in the AuthServer  project, I created an ApplicationUser class that inherits from IdentityUser<Guid> class in .Net Core Identity package with GUID primary key to store basic information about the user such as Email, Username, Password, PhoneNumber. In our subclass, we can extend this class and add additional custom properties to our User class. the mapping table for ApplicationUser class in DB will be AspNetUsers. In this sample, I add a custom property FirstName and LastName to our ApplicationUser class.

Now we need a IdentityDbContext for our custom ApplicationUser class, IdentityDbContext is a predefined EF Core DbContext that exists in .Net Core Identity framework. We create a custom ApplicationIdentityContext class that inherits from Identity base class IdentityDbContext<ApplicationUser>.

If you’re using Visual Studio, you can use the Package Manager Console tools for migrations commands, but in this article, I use EntityFramework command-line tools and dotnet ef  command which is an extension of dotnet CLI for migrations. to use entity framework command-line tools, we need to install dotnet ef as global or local tools with this dotnet tool install --global dotnet-ef command  and also we need to install Microsoft.EntityFrameworkCore.Tools NuGet package to our project that we want to execute our migration commands. I use add migration command to generate first migration for our project, to see help about add migration command you can use this dotnet ef migrations add -h help command and for more information, please read this article about entity framework command-line tools. but this command will not work because migrations do not know how to generate data context so for this purpose we need to implement IDesignTimeDbContextFactory an interface so I create ApplicationIdentityDbContextFactory  that inherits from IDesignTimeDbContextFactory and implement its CreateDbContext for generating a DbContext. when we implement this interface EF tools can discover our factory class for creating our ApplicationIdentityDbContext and configure need configuration for our DbContext for example here we read connection string from our appsettings but for reading more about managing secret in .net core and safer ways use these links: Link1, Link2. after that, we do a migration on our DbContext and apply it to the database. That is why we need to implement IDesignTimeDbContextFactory interface.

Now after we implement this interface we can use command-line tools like cmd or PowerShell to execute migration command on our Identity project.

The next thing to do is adding dependency injection for our ApplicationIdentityDbContext with using AddDbContext<ApplicationIdentityDbContext> and add it to Service Collection and then we setup Identity provider on .Net Core Middleware with using AddIdentityCore<ApplicationUser>. I add all needed configurations for our infrastructure layer in an extension method for the cleaner startup which is AddInfrastructure extension method.

For adding necessary services we should add AddApplication extension method to our service collection in the startup.

UserManagementService and RoleManagementService is two service manager on top of built-in .Net Core UserManager nad RoleManager that we created and added them to our service collection for dependency injection.

this is our startup class for AuthServer until now:

Configure Resources Server

Now let’s create our first API on Resource Server, I named it ProtectAPIResource and create a folder for this API with the name of ProtectAPIResource and inner this folder we use this dotnet new webapi -n ProtectAPIResource command and add this project to our solution with this dotnet sln "Jwt Authentication.sln" add "Resources Server/ProtectAPIResource/ProtectAPIResource.csproj" command in the root of our sample that the solution file exists.

In Resource server, we should validate the requested token for access to protected resources so for validating token we need a signing key that in this case, we should use a public key so we setup the required configuration for jwt signing key.

Then we should configure our authentication middleware with Jwt so I use our predefined AddJwtAuthentication extension method in ConfigureServices method our startup and add it to our service collection. this is our startup class for our ProtectedResourceAPI :

Implementing Account Controller

Now let’s Continue with creating our the First Controller class with the name of AccountController which contains three actions: LoginUser, RegisterUser, UpdateUserPassword. I use CQRS in most of my projects for simplicity and increase performance for reading and writing also Thin controllers for less code in my controllers. you can read more about CQRS and thin controller, here and here. for implement the CQRS pattern I use the MediateR package by jimmy bogard.

Ok, let’s implement the first action for our controller which is RegisterUser. in this action, this action gets a RegisterUserCommand and send this command to in-memory command bus (dispatcher) to execute our command by using a command handler, in this sample I use MediateR for doing this.

for testing creating a user on the AuthServer, I use PostMan as a client but you can use other applications like angular for testing our APIs.

In RegisterUserCommandHndler, We create a new user with using passed command information and use of our UserManagerService, after a user created successfully we raise an event UserRegisterdEvent.

Well, now for our LoginUser action we get an SignUserCommand and pass it to our command dispatcher for executing. then command dispatcher send our command to the corresponding command handler that in this case is  SignUserCommandHandler to handle our command.

In this Handler, we first find a user with the credential that passed with the command to our handler if the credential is valid and there is a user with this credential in our database, we generate a token for this user with all roles and custom claims using our JwtService. after generating our token we publish an event UserLoggedInEvent.

This is a test of the LoginUser action method on AccountController on our AuthServer with using PostMan client:

Another action method in AccontController is UpdatePassword which gets a UpdateUserPasswordCommand and dispatches this command to corresponding command handler that is UpdateUserPasswordCommandHandler for executing.

In this command handler, we change user password to a new password with incoming UserPasswordCommand and using our UserManagerService then and then update the database with a new password for this specific userId user and finally raise an PasswordChangedEvent.

Calling UpdateUserPassword action AccountControler on our AuthServer with PostMan client gives us a 401 unauthorize response because this action is protected on the account controller and for access to this protected resource we should authorize so we need to pass a jwt access token with Authorization Header of our request. after login to AuthServer we get an access token and we send this access token with UpdateUserPassword request through Authorization Header and our request will be authorized successfully with this token and access this protected action and we get a 204 No Content response.

Calling Protect Resource API with Using a Token

Now after implementation account controller on AuthServer and create basic action for AuthServer we can call our protect API on Resources Server.

On our ProtectedAPIResource project, we have a WeatherForecast Controller with a Protected Get action method with an Authorize attribute with AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme that specify we use Jwt token for authorizing a request to access to this action.

for calling this Get action on WeatherForecast Controller we use PostMan with a Get Request method and specifying an Authorization Header with our access token for authorizing our request for access to protected Get action on this controller and after this call, it returns a list of weather information with status code 200 Ok.

Note: I Added required Postman requests in a collection and add it to the client folder in the root of our project repository. you can use it for testing APIs.

This was our second part of Jwt Authentication in .Net Core, I hope to enjoy this post and it was helpful 🙂

In the future post of these series, I talk about refresh token and cancel token.

Tags

Mehdi Hadeli

I’m a software engineer with +10 years of experience in developing and designing distributed applications built on top of cutting-edge technologies with interest in Microservices, DDD.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.