Tech

SECURING WEBAPI USING JSON WEB TOKEN (JWT) IN WEB API C#

Updated to use latest :  System.IdentityModel.Tokens.Jwt 6.8.0 Jwt Web Api c# Development Environment: for ASP.NET Core 6 based implementation here is the link. ASP.NET...

· 6 min read >

Updated to use latest :  System.IdentityModel.Tokens.Jwt 6.8.0

Jwt Web Api c# Development Environment:

Visual Studio 2019 & Latest .NET Framework 4.7.2

for ASP.NET Core 6 based implementation here is the link.

ASP.NET CORE 6 JWT Authentication

Let’s ask our self some Prerequisite question before start working on JWT Implementation

Prerequisite Knowledge 

Do you know what is a HttpMessageHandler & DelegatingHandler in ASP.NET  ? If not then I would highly recommended reading this post . httpmessagehandler c# example

Do you know basics of jwt web api JWT(Json Web Token)? If not then Visit  jwt.io for learning more about JWT . Chris has also provided good details about JWT info.

Implementation :

Let’s move on to actual work that need to be done. Two basic mechanism for securing WEB API

  • Using Traditional Cookie based authentication
  •  Using Token Based Authentication – In simple words our goal is to secure data transmission between two endpoints JWT is a way to achieve.
    • Json Web Token Consist of Three parts
      • Header
      • Claim
      • Signature

Both jwt authentication methods and Cookie based have pros and cons and that is out of the scope of this article and either of the methods should be adopted based on circumstances. we will follow Token Based Authentication Using jwt authentication. The mechanism is not complex once we get understanding of workflow. So the key is not to jump on code right away but understand  flow of request. Let’s do it

  1. User Enter username and password on mobile/web application and press login.
  2. User Credentials are sent to WEB API endpoint.
  3. We Intercept HTTP Request and check if the header has JWT token(it will not be there for the first request) if not then we verify username and password and if credentials are correct, we create a JWT token using the library and send it is back in the response body.Next time client request protected resource with this token in a header so for the subsequent request we intercept HTTP request and validate token.

Image below shows flow of information between your browser/mobile   and server.

Let’s start coding for our JWT WEB API C#

1- Create a New Project and Name your Project and Solution and Select ASP.NET application. In Latest Visual studio there are couple of steps to do it.

Give your project a name and Click Create

Select Web API as project type and click Create.

The default project layout is listed below for reference.

if you prefer reading then continue below with the article. Video Version of this article below make sure you subscribe.

4. Now Let’s install this Nuget Pacakge.  System.IdentityModel.Tokens.Jwt -Version 6.8.0

This is done using package manager console. You can access it under Tools dropdown in NuGet Package Manager as directed the next listing .

Run the command below in the console. screenshot reference below.

Install-Package System.IdentityModel.Tokens.Jwt -Version 6.8.0

We need two model classes or in old school term POCO classes to encapsulate response and request. you can add them where seem appropriate.

 public class LoginRequest
    {
        public string Username { get; set; }
        public string Password { get; set; }

    }

you will need following two import for LoginResponse Classs

using System.Net.Http;
using System.Web.Http;

public class LoginResponse
    {
        public LoginResponse()
        {

            this.Token = "";
            this.responseMsg = new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.Unauthorized };
        }

        public string Token { get; set; }
        public HttpResponseMessage responseMsg { get; set; }
    }

Verifying The jwt web api c#  :

5. we will need to add a  class at root level that extends from DelegatingHandler  and we will override SendAsync  method. Please note the purpose of adding this class is to intercept incoming HTTP Request and Retrieve and Validate Token.

Until this point if you don’t know what is purpose of DelegatingHandler is Please read this post before moving forward. HTTP Message Handler in Web API

Let’s name this class TokenValidationHandler see code snippet below and follow it carefully. click on Add to add a new class.

Don’t just copy paste every thing listed below along with namespace as your name can be different if you still want to copy make sure you use your namespace . Make note of main package used in first line using microsoft identitymodel tokens

using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace WebAPIWT
{
    internal class TokenValidationHandler : DelegatingHandler
    {
        private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
        {
            token = null;
            IEnumerable<string> authzHeaders;
            if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
            {
                return false;
            }
            var bearerToken = authzHeaders.ElementAt(0);
            token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
            return true;
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpStatusCode statusCode;
            string token;
            //determine whether a jwt exists or not
            if (!TryRetrieveToken(request, out token))
            {
                statusCode = HttpStatusCode.Unauthorized;
                //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
                return base.SendAsync(request, cancellationToken);
            }

            try
            {
                const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
                var now = DateTime.UtcNow;
                var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));


                SecurityToken securityToken;
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                TokenValidationParameters validationParameters = new TokenValidationParameters()
                {
                    ValidAudience = "http://localhost:50191",
                    ValidIssuer = "http://localhost:50191",
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    LifetimeValidator = this.LifetimeValidator,
                    IssuerSigningKey = securityKey
                };
                //extract and assign the user of the jwt
                Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
                HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

                return base.SendAsync(request, cancellationToken);
            }
            catch (SecurityTokenValidationException e)
            {
                statusCode = HttpStatusCode.Unauthorized;
            }
            catch (Exception ex)
            {
                statusCode = HttpStatusCode.InternalServerError;
            }

            //@josbol mentioned this issue in 2018
            
            //Since this solution pushes the request further down the pipeline, instead of immediately returning, this 
            //solution consumes more resources for each request, so this might not be the right solution for everybody.
            //Alternatively one could manually add the missing headers before returning,
            //but I don't think that would be a good idea.

            //- this line would return correct CORS header.- credit to 
            //return base.SendAsync(request, cancellationToken);

                    return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode) { });
        }

        public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
        {
            if (expires != null)
            {
                if (DateTime.UtcNow < expires) return true;
            }
            return false;
        }

    }
}

we wrote a custom Message Handler, we defined our own class that derived from the DelegatingHandler class and we override SendAysnc method.

This code will intercept HTTP request and validate the JWT.

 Generating the JWT Token  Web Api C#:

6. Next thing is we need to create a JWT after verifying the username and password with our database. For this purpose, I am going to create a controller. Let’s name it LoginController.cs 

  • Don’t just copy paste every thing listed below along with namespace as your name can be different if you still want to copy make sure you copy controller and required imports.
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Web;
using System.Web.Http;


namespace WebAPIWT.Controllers
{
    public class LoginController : ApiController
    {
        [HttpPost]
        public IHttpActionResult Authenticate([FromBody] LoginRequest login)
        {
            var loginResponse = new LoginResponse { };
            LoginRequest loginrequest = new LoginRequest { };
            loginrequest.Username = login.Username.ToLower();
            loginrequest.Password = login.Password;

            IHttpActionResult response;
            //HttpResponseMessage responseMsg = new HttpResponseMessage();
            bool isUsernamePasswordValid = false;

            if (login != null)
                isUsernamePasswordValid = loginrequest.Password == "admin" ? true : false;
            // if credentials are valid
            if (isUsernamePasswordValid)
            {
                string token = createToken(loginrequest.Username);
                //return the token
                return Ok<string>(token);
            }
            else
            {
                // if credentials are not valid send unauthorized status code in response
                loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                response = ResponseMessage(loginResponse.responseMsg);
                return response;
            }
        }


        private string createToken(string username)
        {
            //Set issued at date
            DateTime issuedAt = DateTime.UtcNow;
            //set the time when it expires
            DateTime expires = DateTime.UtcNow.AddMinutes(10);

            //http://stackoverflow.com/questions/18223868/how-to-encrypt-jwt-security-token
            var tokenHandler = new JwtSecurityTokenHandler();

            //create a identity and add claims to the user which we want to log in
            ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.Name, username)
            });

            const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
            var now = DateTime.UtcNow;
            var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
            var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);


            //create the jwt
            var token =
                (JwtSecurityToken)
                    tokenHandler.CreateJwtSecurityToken(issuer: "http://localhost:50191", audience: "http://localhost:50191",
                        subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
            var tokenString = tokenHandler.WriteToken(token);

            return tokenString;
        }

     }
}

we have CreateToken method that generates a token and Authenticate method receive an HTTP request and we validate the username and password and if they are valid we send back the Token.

7. Next step is where we will register Message Handler class. Add following lines in WebApiConfig.cs

   config.MessageHandlers.Add(new TokenValidationHandler ());

8 . Securing our controller ; Let’s decorate our Values Controller with Authorize attribute and Run the project press F5

 9 .  jwt web api c# Testing

you can use any Rest client tool to make a post call to these endpoints like Postman, fiddler or chrome extension . we will use Talend API Tester – Free Edition.

https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?hl=en

so here is the work flow

1- Obtaining JWT token for webapi c# :  Make a POST call to Authenticate endpoint by providing username/password  to get the token.

2- Using the Token to access secure endpoint of jwt web api C#:  we will use token to get access to secure resource in our case any endpoint in values controller.

As you can see I added the token in the header do notice syntax  Bearer  token .  we accessed our secured resource using JWT. It is valid for about of time we set when we generated it . if we temper with it unauthorized  code is returned.

Code for JWT Web Api:

https://github.com/seanonline/Webapi_JWT_Authentication

Step by Step Video below along with the Code above can save your time.

https://youtu.be/1lY0KQo-3ZA

Recommended Next Reading :

net core six article

ASP.NET CORE 6 JWT Authentication

Apollo11 in Tech
  ·   9 min read
  • Hi. This is very nice and helpful tutorial. Just one question, What will be the method to refresh the token after token expire? By passing old token can we refresh token and recrate it again?

    • I am glad it was helpful. I have not had a chance to write the refresh token workflow but I do have a code on my local machine that implements it if you are interested in I can share it.

      so basically idea is that when you initially generate the token you save it in the database.

      when it expires you use the expired token compared it with what is in the database to validate if it is a valid token and then you generate the new token. I do have a code for that using SQL server I think I wrote a few months back.

      • Great. I am storing this token in angular local storage instead of SQL. Please share the code so that I can re-write it according to my angular storage. Thank you very much.

    • Hello, oscar I will have to test it to see the actual behavior but my educated guess will because expire specify when the token will expire so if you set it to null it will probably be valid all the time

  • Thank you so much, this tutorial was very helpful.
    One question when the token expires I get a white screen (no error code). Is there a way to return a message when the token expires?

    • When the token expire the control hit this exception section and the return status is set to unauthorized. if you using postman you will see the status of request will be 401 unauthorized not 200 which mean OK.

      you can use a content property to pass in additional message after token is rendered invalid.

      Two ways to do it

      return new HttpResponseMessage( HttpStatusCode.OK ) {Content = new StringContent( “Your message here” ) };

      or for json serialized content

      return new HttpResponseMessage( HttpStatusCode.OK ) {Content = new StringContent( SerializedString, System.Text.Encoding.UTF8, “application/json” ) };

      In short this is change you will need to make .

      inside the file TokenValidationHandler.cs –> SendAsync method last line approx#87 would become
      like below

      return Task.Factory.StartNew(() => new HttpResponseMessage(statusCode) { Content = new StringContent("There was problem on server" +
      "when verifying your authorization") });

  • Thank you so much maaan it really saved my day!!! After searching and researching whole day i found this and it was really helpful!

  • >