In-Memory Logins in ASP.NET Core with JWT
Some line of business (LOB) applications or internal systems have a predefined set of users and corresponding login information. In situations like those, using a database mechanism to store user information is an overhead. Combine this overhead with Microsoft’s heavy-duty implementation of the Identity framework with persisting concerns in the Entity framework, and you have a recipe for a headache. Those frameworks might be useful in certain situations, but here we have a limited scope and, in situations like this, we can, instead, take a lighter approach towards this functionality.
In this post, we will look at a way to implement authentication functionality in an ASP.NET Core Web application. We will go through all the steps and how things are put together to achieve the desired result. The source code is available in this GitHub repository.
- Some form of a user-store/service where we can store our user’s credentials.
- A sign-in mechanism, which will also respond with JSON Web Tokens (JWTs).
- JWT wiring in ASP.NET Core and handling.
I’ve created a .NET Core Web API project using a Visual Studio project template and this comes with a default controller:
The following picture shows data returned by WeatherForecastController:
Protecting the API With JWTs
First, we will need a Nuget package to include JWTs in our ASP.NET Core pipeline:
Next, I’ve updated the startup.cs file to wire-up a JWT:
With these changes in place, I created another method in WeatherForecastController and protected it with the
If we try to call this end-point, it will result in a 401 Unauthorized error as shown below:
So, this confirms that our end-point is now protected from unauthorized access.
Let’s continue and see how we can allow certain users access to protected endpoints.
User Entity and UserService Interface
Let’s define a User entity and interface for a service called UserService:
Here is our
User class. As you can see, it is very basic:
Next, we created an interface named
IUserService as shown below:
Let’s implement the
IUserService interface. As we can recall from the introduction, we need some sort of user-store to store credentials. This service will act as a data-store for our application’s users as well:
You can see that I defined a dictionary with
_users to store credentials (notice the use of a tuple in the
_users dictionary) and I am populating this dictionary via the constructor. Next, let’s see how the
ValidateCredentials method is implemented:
I think the code is very simple to follow. We are just finding the user in the dictionary, checking the password, and, if everything is ok, returning the result.
Next, we will register
UserService with an IoC container in the
ASP.NET Core UserService Registration
Please see the image below where I am populating
UserName with passwords in a dictionary, passing it to
UserService, and then registering
UserService as singleton:
So, now wherever in our code we need to access the
UserService, it will be available via Dependency Injection.
Login, Claims, and Response
So, we have a user-store, a way to validate credentials, and, now, we need a way to allow users to get JWTs by providing their user name and password. Once this token is received, users or client applications can include it as an Authorization Header to access protected endpoints.
This code is mostly infrastructure code, and there are a lot of examples online that utilize a similar mechanism for JWTs. So, we will start by creating a new controller called
We are injecting
UserService via dependency injection, and we have only one
SingIn endpoint, which accepts a
SignInModel via HTTP
I have added a few classes to the project. These classes are used as a data-bag to move data around, and we will see how those are being used in a moment:
Let’s see the code for the
Here, we are validating credentials, creating a couple of claims, creating an instance of
UserAuth (see class above), and returning it to the caller.
Before we check out the code for
TokenUtils.BuildUserAuthObject(), let’s see the output of this method. I used Postman to make an HTTP
POST to the
signin endpoint with
This call returns
bearerToken along with some other data. Now we can use this
bearerToken to access restricted endpoints. We just need to add it as an Authorization header in the HTTP request:
As you can see, this time, data was returned from the protected endpoint. So a bearer token is returned when we make a call with a valid username and password, and then we use that bearer token with other HTTP requests to access protected data.
Now, let’s see how the bearer token is being generated. It is very boiler-plate code, and if something is not clear, feel free to ask in the comments:
Our implementation is finished, but it can be improved. We are currently storing the password in memory and in plain-text. However, if we want, we can store the password hash instead.
I added the Bcrypt.Net-Next nuget package to the solution:
Now in the UserService constructor, use its HashPassword method as shown below:
We also need to update the verify mechanism for the password as shown below:
With these changes in place, we have a hashed password in memory, and it’s more secure.
We saw how we can implement in-memory logins for simple applications. Even small, this is a very secure mechanism utilizing JWTs and BCrypt nuget packages. You can further extend it by connecting it with database persistence or adding more capabilities to the in-memory store, e.g. Registering New Users can be done easily.
You can download the source code from this git repository. If you have some comments or questions, feel free to ask. Till next time, happy coding.
Credit: Source link