
In-Memory Logins in ASP.NET Core with JWT
Introduction
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.
Requirements/Considerations
We’ll need:
- 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 [Authorize]
attribute:
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:
UserService Implementation
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 startup.cs
file.
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 AuthController
:
We are injecting UserService
via dependency injection, and we have only one SingIn
endpoint, which accepts a SignInModel
via HTTP POST
.
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 SignIn
method:
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 UserName
and Password
:
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.
Token Generation
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:
Hashing Passwords
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.
Summary
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