Making WEB API Call Using JWT Token

This article cover in detail JWT token usage in .Net MVC Web application.

  • So why JWT (JSON Web Token) required while making a WEB API call.

JSON Web Tokens are a good way of securely transmitting information between client application and WEB API.

How to make WEB API call using JWT token

Before understanding code lets first understand architecture of the project.

As highlighted in below screenshot of solution explorer. 

  • MVC web application: This is main web project which will show result to end user on web pages.
  • Web API Service: web application makes call to service layer to retrieve data from database.
  • Data Access Layer: Web API connect to SQL database through data access layer.

Making WEB API Call Using JWT Token

Step 1:  .Net MVC Web application calling JQuery function MakeWebApiCall

Below code is written on “.cshtml” under view folder. This code makes call to Jquery function “MakeWebApiCall.

Making WEB API Call Using JWT Token

Step 2: MVC Web Application under script folder

 Below is Jquery function makes a call to Web API to get data from database and bind that data to drop down control on ".cshtml" view.

 This function is written on apiaccess.Js file.

function MakeaWebApiCall(webapiurl) {

    try {
        $.ajax({
            async: true,
            type: "GET",
            url: webapiurl,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            headers: {
                'CustomAuthorization': 'Bearer ' + getToken()
            },
            success: function (data)
            {

              LoadDropdown(data);              
            }
        });
    }
    catch (ex) { }
}

While making a WebAPI call from web application you need to provide JWT token for validation. If this token is not provided then request to WEB API will be failed with Unauthorize error. In this way if someone try to call Web API URL from outside of application it will fail with error. For every call a fresh token is created and stored in session.

Step 3:  MVC Web Application under script folder

 Below two Jqery functions are written in apiaccess.Js file to get new JWT token.

function getToken() 
{
    let accessToken = sessionStorage.getItem("AccessToken");
    
     if (accessToken) 
       {
         let expiresOn = sessionStorage.getItem("TokenExpiry");
 
         if (!expiresOn && new Date(expiresOn) < new Date()) 
         {
            getRefreshToken();
         }
    }
    else 
    {
        getRefreshToken();
    }

    accessToken = sessionStorage.getItem("AccessToken");
    
    return accessToken;
}
function getRefreshToken() {
    $.ajax({
        async: false,
        type: "GET",
        url: '/home/refreshtoken',
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (data) {
            sessionStorage.setItem("AccessToken", JSON.parse(data).Token);
            sessionStorage.setItem("TokenExpiry", JSON.parse(data).ExpiryDate);
        },
        error: function (e) {
            console.log(e.message);
        }
    });
}

Step 4: Calling MVC Web application controller

 From step 3 getRefreshToken() token Jquery function makes a call to RefreshToken() funtion written in homeController of MVC Web application.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Reporting.Models;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;

namespace Reporting.Controllers
{
    [CustomAuthorize]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        private void ConfigureHttpClient(HttpClient client)
        {
            string apiName = "API/";
            string apiUrl = ConfigurationManager.AppSettings["WebAPIUrl"];

            if (apiUrl.Contains(apiName))
            {
                apiUrl = apiUrl.Replace(apiName, "");
            }

//            Log.Info("ConfigureHttpClient():"+ apiUrl);
            client.BaseAddress = new Uri(apiUrl);//new Uri(ConfigurationManager.AppSettings["WebAPIUrl"]);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }

        public JsonResult RefreshToken()
        {
            
            string client = Encryption.DecryptFromBase64String(ConfigurationManager.AppSettings["ClientSecret"].ToString() 
                                                                     , Encoding.Base64Decode(ConfigurationManager.AppSettings["EncKey"]));

            string phrase = Encryption.DecryptFromBase64String(ConfigurationManager.AppSettings["PassPhrase"].ToString()
                                                                     , Encoding.Base64Decode(ConfigurationManager.AppSettings["EncKey"]));

            var clientSecret = CryptoUtil.OpenSslEncrypt(client, phrase);

            TokenRequest tokenRequest = new TokenRequest
            {
                ClientSecret = clientSecret,
                Username = User.Identity.Name
            };

            using (var httpClient = new HttpClient())
            {               
                var response = httpClient.PostAsJsonAsync(ConfigurationManager.AppSettings["WebAPIAuthUrl"] + "Authenticate", tokenRequest).Result;
                                
                if (!response.IsSuccessStatusCode)
                {
                    //LOG ERROR WITH Statuscode
                    var responseContent = string.Concat("Status Code:" + response.StatusCode.GetHashCode(), " Reason:", response.ReasonPhrase);
                    // TODO:  log error
                    throw new Exception(responseContent);
                }
                 
                TokenResponse tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(response.Content.ReadAsStringAsync().Result);
                this.Session["AccessToken"] = tokenResponse;

                //Log.Info("Token Response in RefreshToken:"+ tokenResponse.ToString());
                return Json(JsonConvert.SerializeObject(tokenResponse), JsonRequestBehavior.AllowGet);
            }
        }
    }
}

Step 5

From step 4 Refreshetoken() function makes a call to  WEBAPI “Autheticate”.

Below Web API class is having Authenticate function which will generate token and send back to step 4.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Service.Models;
using System.Configuration;
using Common.All;
using Common.All.Logging;

namespace WEBAPI.Service.Controllers
{
    
    public class AccountController : ApiController
    {
        [HttpPost]         
        public IHttpActionResult Authenticate([FromBody] TokenRequest tokenrequest)
        {
            //Log.Info("Service-Account controller-authenticate called.");
            IHttpActionResult response;
            using (HttpResponseMessage responseMsg = new HttpResponseMessage())
            {
                bool isclientSecretValid = false;

                if (tokenrequest != null)
                {
                    string ServiceSecret = Encryption.DecryptFromBase64String(ConfigurationManager.AppSettings["ServiceSecret"].ToString()
                                                                     , Encoding.Base64Decode(ConfigurationManager.AppSettings["EncKey"]));

                    string Servicephrase = Encryption.DecryptFromBase64String(ConfigurationManager.AppSettings["Servicephrase"].ToString()
                                                                             , Encoding.Base64Decode(ConfigurationManager.AppSettings["EncKey"]));

                    string clientSecret = CryptoUtil.OpenSslDecrypt(tokenrequest.ClientSecret, phrase);

                    isclientSecretValid = ServiceSecret == clientSecret ? true : false;
                }
                else
                {
                    // if credentials are not valid send unauthorized status code in response
                    responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                    response = ResponseMessage(responseMsg);
                    return response;
                }
                 
                // if credentials are valid
                if (isclientSecretValid)
                {
                    TokenResponse tokenResponse = GenerateToken(tokenrequest.Username.ToLower());
                    return Ok(tokenResponse);
                }
                else
                {
                    // if credentials are not valid send unauthorized status code in response
                    responseMsg.StatusCode = HttpStatusCode.Unauthorized;
                    response = ResponseMessage(responseMsg);
                    return response;
                }
            }
        }

        private TokenResponse GenerateToken(string username)
        {
            //Log.Info("Service-Account controller-GenerateToken called.");
            //Set issued at date
            DateTime issuedAt = DateTime.UtcNow;
            //set the time when it expires
            DateTime expires = DateTime.UtcNow.AddDays(7);
            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)
            });

            string sec = ConfigurationManager.AppSettings["secretkey"].ToString();
            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: ConfigurationManager.AppSettings["issuer"], audience: ConfigurationManager.AppSettings["audience"],
                        subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
            var tokenString = tokenHandler.WriteToken(token);


            //Log.Info("Service-Account controller-GenerateToken-tokenString:"+ tokenString);

            TokenResponse tokenResponse = new TokenResponse
            {
                Token = tokenString,
                ExpiryDate = expires
            };

            return tokenResponse;
        }
    }
}

Below two extra classes required for above class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Service.Models
{
    public class TokenRequest
    {
        public string Username { get; set; }
        public string ClientSecret { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Service.Models
{
    public class TokenResponse
    {
        public string Token { get; set; }
        public DateTime ExpiryDate { get; set; }
    }
}

Step 6: WEB API Service project 

When MVC web application makes a token based call to WEB API, below code in Web API service project validates in coming request based on token provided.

Register TokenValidationHandler class. This class validate JWT token passed from MVC application. Token Validation handler class exist in Web API service Layer under Model folder.

Making WEB API Call Using JWT Token

Below is TokenValidationHandler.cs

using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks; 
using System.Web;
using Service.Models;

namespace Service
{
    internal class TokenValidationHandler : DelegatingHandler
    {
      //  private ILogger _logger;
        private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
        {
            //Log.Info("TryRetrieveToken() called.");
            token = null;
            IEnumerable<string> authzHeaders;
            if (!request.Headers.TryGetValues("CustomAuthorization", 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)
        {
//            _logger = new FileLogger();
        //Log.Info("SendAsync() called inside service TokenValidationHandler.cs.");
        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);
            }

        //    _logger.LogInfo("token: "+token + Environment.NewLine);

            try
            {
                string sec = ConfigurationManager.AppSettings["secretkey"].ToString();
                var now = DateTime.UtcNow;
                var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));                
                SecurityToken securityToken;
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                TokenValidationParameters validationParameters = new TokenValidationParameters()
                {
                    ValidAudience = ConfigurationManager.AppSettings["audience"],
                    ValidIssuer = ConfigurationManager.AppSettings["issuer"],
                    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);
               // _logger.LogInfo("before calling base.SendAsync(request, cancellationToken);");
                return base.SendAsync(request, cancellationToken);
            }
            catch (SecurityTokenValidationException e)
            {
                statusCode = HttpStatusCode.Unauthorized;
               // _logger.LogError("Exception in TokenvalidationHandler:" + statusCode.ToString(), e);
            }
            catch (Exception ex)
            {
                statusCode = HttpStatusCode.InternalServerError;
               // _logger.LogError(ex.Message,ex);
            }
            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;
        }        
    }
}

Step 7: Config file settings

Few keys used in code from MVC web application .config file.

<add key="ClientSecret" value="/F0Jv7lxY1S1Iatj60WYs194eZiB0In+AF2PN23REnc=" />
<add key="PassPhrase" value="/F0Jv7lxY1RTZhX9G+8xGrlaJ+CHzode0t3WTncI6HSkP/q+k1knFw==" />
<add key="WebAPIUrl" value="http://localhost:1234/api" /> 
<add key="WebAPIAuthUrl" value=”http://localhost:1234/api/Account/” />
<add key="EncKey" value="STUrIUJDZktQKWdWRVlwLA==" />

Few Keys used in Web API service Layer

<add key="EncKey" value="STUrIUJDZktQKWdWRVlwLA==" />
<add key="ClientSecret" value="/F0Jv7lxY1S1Iatj60WYs194eZiB0In+AF2PN23REnc=" />
<add key="PassPhrase" value="/F0Jv7lxY1RTZhX9G+8xGrlaJ+CHzode0t3WTncI6HSkP/q+k1knFw==" />
<add key="secretkey" 
value="202b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65tr5" />
<add key="audience" value="MVC-Client" />
<add key="issuer" value="API-Service" />


Similar Articles