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
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:
- Autherver: use for authentication, authorization and identity management with using .Net Core Identity
- Resources Server: this part contains API Services that protect from public access by using AuthServer for authorizing access to resources.
- 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.
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
LastName to our ApplicationUser class.
Now we need a
IdentityDbContext for our custom
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
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
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
Implementing Account Controller
Now let’s Continue with creating our the First Controller class with the name of
AccountController which contains three actions:
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.
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
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
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
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.