You are currently viewing Securing .NET Core 3 API Using JWT authentication

Securing .NET Core 3 API Using JWT authentication

Now a days, all the functionalities available in your business applications are required to be available everywhere. Everybody wants the application to be available at their finger tips. Your customers can use your applications on any device including (but not limited to) mobile, iPad, laptop.

In order to make your applications work on variety of devices, applications generally designed to use APIs which can cater the required functionalities to web applications, desktop applications or mobile applications. Most of the times these APIs are using JWT Bearer Token Authentication.

That’s why I thought it would be nice idea to compile the required steps in a blog post. In this article we will use ASP .NET Core identity to validation users and then create the JWT tokens. For the sake of keeping it simple, we will not discuss about refresh tokens in this article..

JWT Internals

JWT stands for JSON Web Token.

Every JWT token has three parts separated by dots, which are as below.

  • Header, which contains two fields – first one is signing algorithm used for signing the token and second one is type which is JWT.
  • Payload, contains claims which are required by your application
  • Signature, to create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

Every part is then Base64 URL encoded, and separated by dots to form the JWT.

Header.Payload.Signature

Signature is used to make sure that the token was not changed along the way.

The output is three Base64-URL strings separated by dots that can be easily passed in HTML and HTTP environments, while being more compact when compared to XML-based standards such as SAML.

If you want to play with JWT and put these concepts into practice, you can use jwt.io Debugger to decode, verify, and generate JWTs.

The Demo Solution

You can download the base solution from my GitHub repository. Follow steps from my previous blog post to create the identity database.

NuGet package references

You will need to add the package reference to NuGet package Microsoft.AspNetCore.Authentication.JwtBearer.

Startup.ConfigurationServices method

The important stuff here is the AddAuthentication call. There you need to specify the AuthenticationScheme and ChallengeScheme.

As you can see, we have enabled validation for issuer, audience and issuer signing key. Below is small explanation of properties which we have sent in TokenValidationParameters.

  • Issuer, is the principal that has issued JWT. If token has different issuer than expected, the validation will fail and caller will receive 401 unauthorized.
  • Audience, is the recipient that JWT is intended for. If token contains different audience than expected, the validation will fail and caller will receive 401 unauthorized.
  • Signing Key, is the key you use for signing the token.
  • ClockSkew, has been set to TimeSpan.Zero. The this represents the time duration after expiry of token, for which the token should be considered valid. I wanted to immediately invalidate the token after expiry time, so I set it to TimeSpan.Zero.

Startup.Configure method

The Authentication middleware is added in Startup.Configure by calling the UseAuthentication extension method on the app’s  IApplicationBuilder.

The call of UseAuthentication registers the middleware which uses the previously registered authentication schemes. Ideally, you should call  UseAuthentication  before any middleware that depends on users being authenticated.

When using endpoint routing, the call to UseAuthentication must go:

  • After UseRouting, so that route information is available for authentication decisions.
  • Before UseEndpoints, so that users are authenticated before accessing the endpoints.

AuthController Code

As you can see, the AuthController code is simple. It has 3 APIs – User Registration, Login and Logout.

In User Registration API, it creates a new identity under the AspNetCore identity database.

The Login API validates user credentials. After successful validation, it creates the token for the user. For demo purpose, we have added only two claims, but you can add more claims as per requirement of your application’s design. You can also create authorization policies to allow user access to some API only if he has specific claim.

The Logout method is pretty empty. Huh.. why ?

How does logout happen ?

I am assuming all of you have a driving license. Every driving license has expiry date. Whenever authority wants to check your driving license, it checks the expiry date and which type of vehicles are allowed to drive using that license.

So if the authority who issued driving license, wants to invalidate someone’s license which has expiry date after years, how will they do this ?

There is no way for them to immediately invalidate the license. They can probably maintain a list of prematurely invalidated driving license. Then next time authority asks you driving license, they will check if this license is not in the invalidated list.

Are you with me so far ?

This description above is perfectly analogous with JWT. Once a user has JWT, there is no way for you to invalidate the token immediately.

So, how will you logout user ?

You can keep the expiry time to be very short. You can also use refresh tokens to refresh the JWT tokens.

The above approach will not immediately logout user. For immediate logout, you can maintain a cache of invalidated tokens on your server. So every-time a request comes, you can check the cache to see if this token was already invalidated.

Download Completed Solution

You can find the completed solution at this place in my GitHub repository.

Test with Postman

The solution has a JSON file which you can import in postman and it will give you requests for user registration, login, get weather forecast and logout APIs. If you execute them in this order, you should be able to see that all APIs are working.

After calling Login endpoint, you will get a token if login is successful. You will have to use this token while calling weather forecast API.

Under Headers, add new header with name Authorization and in its value add Bearer token-value where token-value is the actual token you copied from the output of login API.

Just for testing purpose, if you call get weather forecast before login, you should see 401 unauthorized response. Also, in case, if the token is expired, the get weather forecast will return 401 unauthorized.

I hope you enjoyed this article. Let me know your thoughts.

Leave a Reply

This Post Has 10 Comments

  1. ubong

    it’s a nice article bro

  2. HerringTheCoder

    I think you can omit ModelState.IsValid and request==null checks in controllers, because you are using [ApiController] attribute, which adds automatic validation and a proper response with a list of errors.
    In case there are any validation errors, then your code is never reached, making checks redundant.

    1. Manoj Choudhari
      Manoj Choudhari

      No. This is incorrect assumption. ApiControllerAttribute is derived from ControllerAttribute class. The Controller attribute class is empty and is derived from Attribute class. The documentation says that these classes are the “default” mechanism through which controller classes can be identified by the framework. These attributes do not perform any validations on the incoming request.

      Reference: Source code of .NET Core at: https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ApiControllerAttribute.cs

      1. HerringTheCoder

        Thanks for your response.
        To clarify I was refencing this part of documentation:
        https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-3.1#model-state

        This paragraph can be summarized as:
        1. Validation takes place (automatically) before controller action no matter the attribute. I was a bit unclear on that point before.
        2. In monolith web apps (e.g. returning Razor pages) code should check if the model is valid (as you’ve shown), but it’s only a check against already established (validated) model state, not a validation itself.
        3. With [ApiAttribute] you get automatic 400 response with a list of validation errors.
        Since we’ve established validation takes place before any controller action, then you will never reach IsValid() check unless the model is already valid, thus making it redundant.

        I’ve also tested this behavior with postman/breakpoints and pre-action validation always takes precedence and returns 400+built-in errors if model is not valid.
        Best Regards

      2. Manoj Choudhari
        Manoj Choudhari

        Somehow I missed this. Thanks for sharing the info !

  3. desmondoshiwambo – My name is Desmond Oshiwambo.
    desmondoshiwambo

    Thank you Manoj, your post has been very helpful for me.

  4. Marthin

    How can I use refresh Tokens in ASP.NET Core Authentication?

    1. Manoj Choudhari
      Manoj Choudhari

      I will try to cover it in the upcoming post. Thanks !

  5. Dave

    A great collection of posts for configuring a authentication on a .NET Core WebAPi – really useful and with a Github code to fill in any gaps I got the basic configuration stood up in no time.