• Home
  • About
  • Contact
  • ado.net
  • angular
  • c#.net
  • design patterns
  • linq
  • mvc
  • .net core
    • .Net Core MVC
    • Blazor Tutorials
  • sql
  • web api
  • dotnet
    • SOLID Principles
    • Entity Framework
    • C#.NET Programs and Algorithms
  • Others
    • C# Interview Questions
    • SQL Server Questions
    • ASP.NET Questions
    • MVC Questions
    • Web API Questions
    • .Net Core Questions
    • Data Structures and Algorithms
Showing posts with label Web API. Show all posts
Showing posts with label Web API. Show all posts

Sunday, July 26, 2020

HMAC Authentication in Web API

 Admin     July 26, 2020     .Net, Asp.Net, C#, Web API     1 comment   

In this article, I am going to discuss how to implement the HMAC Authentication in Web API Application. Please read our previous article where we discussed Token Based Authentication in Web API. The most important thing that you need to be considered while developing API is to ensure its security as the API will be exposed over the network and HMAC Authentication is one of the mechanisms to provide security to the Web API Resources. As part of this article, we are going to discuss the following pointers in detail.
  1. What is HMAC Authentication?
  2. Understanding the Keys used in HMAC Authentication.
  3. Uses of HMAC Authentication in Web API.
  4. How does the HMAC Authentication work?
    The flow of HMAC on the Client Side.
    The flow of HMAC on the server-side.
  5. Implementing HAMC in both client and server.
  6. Understanding the Replay Request?
What is HMAC Authentication?
The HMAC stands for the Hash-based Message Authentication Code. From the full form of HMAC, we need to understand two things one is Message Authentication Code and the other one is Hash-Based. So HMAC is a mechanism that is used for creating a Message Authentication Code by using a Hash Function.

The most important thing that we need to keep in mind is that while generating the Message Authentication Code using Hash Function we need to use a Shared Secret Key. Moreover, Shared Secret Key must be shared between the Client and the Server involved in sending and receiving the data.

What are the Keys used in HMAC Authentication in Web API?
First of all, the server needs to generate two keys one is Public Shared APP ID and the other one is Private Secret API Key. Once the keys are generated then it is the responsibility of the Server to provide these keys to the Client using a secure channel like email and this should be done only once and that is too when the client registers with the server.

Once the Client gets the keys, then it is the responsibility of the client to generate a unique HMAC signature (you can also say hash) which not only contain the requested data but also contains all the necessary information which are required by the server to process the request and then the client sends it to the server.

NOTE:
Usually, we need to creates the HMAC Signature (hash) by combining the request data. The Request Data contains the Public APP Id, request URI, request content, HTTP method type, timestamp, and nonce by using the Private Secret API Key (this key is not going to be sent in the request).

Once the server receives the request, then it tries to generate the hash (unique HMAC Signature) by using the data received from the client request. While the Server Generates the hash, it needs to use the same Private Secret API Key (which is used by the client) which was initially shared between the client and the server.

Once the hash (unique HMAC Signature) is generated by the server, then it is going to compare with the hash received from the client. If both the hashes are matched then the server will consider this request as a valid request and proceed else it simply returns unauthorized.

Uses of HMAC Authentication in Web API
The main uses of HMAC Authentication in Web API are as follows.
  1. Data integrity: It means the data sent by the client to the server has not tampered.
  2. Request origination: The request comes to the server from a trusted client.
  3. Not a replay request: The request is not captured by an intruder and being replayed.
If this is not clear at the moment, then don’t worry we will discuss all the above points with a real-time example after some time.

How does the HMAC Authentication work?
As of now, we have discussed the basics of HMAC Authentication in Web API from both the client and server point of view. Now let’s discuss the flow of Client and Server in details i.e. how the HMAC authentication works.

As we already discussed, first of all, the server should create and provide the two keys (Public Shared APP Id and Private Secret API Key) to the client. It is the responsibility of the client not to share the Private Secret API Key with anyone and moreover, the Client needs to store the Private Secret API Key securely, mostly in a database or in the config file.
HMAC Authentication in Web API

Let first discuss the flow of Client in HMAC Authentication.

The HMAC Flow on the Client Side:
First, the client needs to create a string (MAC – Message Authentication Code) which will contain all the request data that the client wants to send to the server. Generally, the string contains the following parameters
  1. HTTP method
  2. APP Id
  3. Nonce
  4. The request URI
  5. Request timestamp
  6. Base 64 string representation of the request payload (request body)
Note:
Here, we need to calculate the Request Time Stamp value by using the UNIX time (number of seconds since Jan. 1st 1970). We need to do this to overcome the possibility of different time zone issues between the client and the server.

The Nonce is a random number or string which is used only once per request. Here we are going to use GUID to create Nonce.

Once the string is generated by combining all the parameters, then it is the responsibility of the client to generate a HASH (unique signature) of the above string by using any hashing algorithm such as SHA256. The important thing here you need to remember is that while generating the unique signature (hash), you need to use the Private Secret API Key which was initially provided by the server.

Once the unique signature (hash) is generated by the client, then the client needs to send that signature (hash) in the request header using a custom scheme such as “hmacauth”.

Here you can use any header but for the simplicity of this demo, we are going to use the Authorization header but it is not mandatory you can use any header.

The data in the header will contain the public shared APP Id, the request time stamp, and the nonce separated by a colon ‘:’. As we are going to use the Authorization header, so the format for the Authorization header should be as shown below:
[Authorization: hmacauth APPId:Signature:Nonce:Timestamp]
The Flow of HMAC on the server-side:
Step1:
The Server receives the request which contains the request data and the Authorization header. The Authorization header contains the HAMC signature. From the Authorization header, the server needs to extracts the values such as APP Id, Signature, Nonce, and Request Timestamp.

Step2:
Once the server extracts the values from the Authorization header, then by using the APP ID (this value we will get in Step1), the Server tries to get the Private Secret API Key which is generally stored in some secure repository such as a database or in the configuration file.

Step3:
Once the server gets the Private Secret API Key, then it tries to rebuild the string by combining the received request content data along with the extracted data (such as APP Id, Nonce, and Request Timestamp which are extracted in Step1). The important thing here we need to understand is that the parameters order and the encoding format should be the same as followed by the client.

Step4:
Once the string is built, then it is the responsibility of the Server to generate a hash value by using the same Hashing algorithm used by the client i.e. SHA256 using the Private Secret API Key which is already retrieved by the server in Step2.

Step5:
Once the hash is generated on the server, then the server compares the generated hash with the hash sent by the client, if both the hashes are matched then the server will consider this request as a valid request and process that request else it simply returns 401 unauthorized.

As of now, we have discussed lots of theory, you may have some doubts. Let’s clear your doubt with the practical implementation of the above theory. I divided the implementation into three sections.
  1. Section1: Generating the public APP ID and Shared Private API Key
  2. Section2: Building the Client Application
  3. Section3: Building the Server (Backend API)
Section1: Generating the public APP ID and Shared Private API Key
Let’s first discuss how to generate the Public Shared APP ID and a strong 256 bits key which will act as our Private Secret API Key. This is usually done on the server and then provided to the client using a secure mechanism as we already discussed.

Here, we will use the Symmetric Key Cryptographic Algorithm to generate the 256 bits key which will be our Private Secret API Key and GUID to generate the Public Shared APP ID. To do so, create a console application and modify the Main method of the Program class as shown below to generate the APP ID and API Key:
using System;
using System.Security.Cryptography;
namespace GeneratedClientAppIDAPPKey
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var cryptoProvider = new RNGCryptoServiceProvider())
            {
                var APPID = Guid.NewGuid();
                byte[] secretKeyByteArray = new byte[32]; //256 bit
                cryptoProvider.GetBytes(secretKeyByteArray);
                var APIKey = Convert.ToBase64String(secretKeyByteArray);
            }
        }
    }
}
So for this demo let’s assume that our APP ID is 65d3a4f0-0239-404c-8394-21b94ff50604 and our API Key is WLUEWeL3so2hdHhHM5ZYnvzsOUBzSGH4+T3EgrQ91KI= and we also assume that we have provided these two Keys to our client using a secure channel.

Section2: Building the Client Application
In this section, we are going to create the client application (Console Application) which will use HMAC Authentication. Let’s discuss the step by step process to achieve this.

Step1: Creating a Console Application and installing the necessary Packages from NuGet
Create a new console application with the name HMACClient, and then install the following package which will help us to issue HTTP requests to the server.

Install-Package Microsoft.AspNet.WebApi.Client -Version 5.2.7

You also need to include a reference to the System. Web dll. To do so, right-click on your project in the Solution Explorer and choose Add Reference and then search for System.Web and add that reference as shown in the below image.
HMAC Authentication in ASP.NET WEB API

We also need the System.Runtime.Caching library for storing the data in cache memory. To do so, right-click on your project in the Solution Explorer and choose Add Reference and then search for System.Runtime.Caching and then add that reference as shown in the below image.
HMAC Authentication in C#

Step2: Adding the Model Classes
In our client application, we will make use of the HTTP Post request in order to show how we can include the request body within the HMAC Signature. So. add a class file with the name “Order” and then copy and paste the following code:
using System;
namespace HMACClient
{
    public class Order
    {
        public int OrderID { get; set; }
        public string CustomerName { get; set; }
        public string CustomerAddress { get; set; }
        public string ContactNumber { get; set; }
        public Boolean IsShipped { get; set; }
    }
}
Step3: Call the API using HTTPClient
Now we will see, how to use the HTTPClient library installed in Step1 to issue an HTTP Post request to the Web API (that we are going to build in the next section i.e. in Section3) using HMAC Authentication. So open the Program.cs file and then copy and paste the following code:

Note: In the next step we will create the HMACDelegatingHandler which we are going to use in this step. So don’t be confused by getting the error for HMACDelegatingHandler.
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace HMACClient
{
    class Program
    {
        static void Main(string[] args)
        {
            RunAsync().Wait();
            Console.ReadLine();
        }
        static async Task RunAsync()
        {
            Console.WriteLine("Calling the back-end API");
            //Need to change the port number
            //provide the port number where your api is running
            string apiBaseAddress = "http://localhost:63493/";
            HMACDelegatingHandler customDelegatingHandler = new HMACDelegatingHandler();
            HttpClient client = HttpClientFactory.Create(customDelegatingHandler);
            var order = new Order
            {
                OrderID = 10248,
                CustomerName = "Pranaya Rout",
                CustomerAddress = "Mumbai|Mahatashtra|IN",
                ContactNumber = "1234567890",
                IsShipped = true
            };
            HttpResponseMessage response = await client.PostAsJsonAsync(apiBaseAddress + "api/orders", order);
            if (response.IsSuccessStatusCode)
            {
                string responseString = await response.Content.ReadAsStringAsync();
                Console.WriteLine(responseString);
                Console.WriteLine("HTTP Status: {0}, Reason {1}. Press ENTER to exit", response.StatusCode, response.ReasonPhrase);
            }
            else
            {
                Console.WriteLine("Failed to call the API. HTTP Status: {0}, Reason {1}", response.StatusCode, response.ReasonPhrase);
            }
        }
    }
}
Explanation of the above code:
What we implemented in the above class is very simple. Here we are issuing an HTTP Post request to the endpoint “/api/orders” including the serialized order object. This endpoint (“/api/orders“) is protected by the Web Server using the HMAC Authentication. Finally, if the returned response status is 200 OK, then we are printing the response returned from Web API Server.

The important thing to keep in mind is that here we are using a custom delegate handler with the name HMACDelegatingHandler. This is the handler that will help us to intercept the request before sending it to the Web API Server. And this is the handler where we need to write the HMAC Signature generation code and also the code that needs to attach the signature in the Authorization header.

Step4: Implement the HTTPClient Custom Handler (HMACDelegatingHandler)
HTTPClient allows us to create a custom message handler which will get created and added to the request message handlers chain. The important thing is that this handler allows us to write the logic as per our requirement. So here we need to write the logic to build the hash (HMAC Signature) and also the logic to set the HMAC Signature in the Authorization header.

To do so, create a class file with the name HMACDelegatingHandler and then copy and paste the following code:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
namespace HMACClient
{
    public class HMACDelegatingHandler : DelegatingHandler
    {
        // First obtained the APP ID and API Key from the server
        // The APIKey MUST be stored securely in db or in the App.Config
        private string APPId = "65d3a4f0-0239-404c-8394-21b94ff50604";
        private string APIKey = "WLUEWeL3so2hdHhHM5ZYnvzsOUBzSGH4+T3EgrQ91KI=";
        protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = null;
            string requestContentBase64String = string.Empty;
            //Get the Request URI
            string requestUri = HttpUtility.UrlEncode(request.RequestUri.AbsoluteUri.ToLower());
            //Get the Request HTTP Method type
            string requestHttpMethod = request.Method.Method;
            //Calculate UNIX time
            DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
            TimeSpan timeSpan = DateTime.UtcNow - epochStart;
            string requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();
            //Create the random nonce for each request
            string nonce = Guid.NewGuid().ToString("N");
            //Checking if the request contains body, usually will be null wiht HTTP GET and DELETE
            if (request.Content != null)
            {
                // Hashing the request body, so any change in request body will result a different hash
                // we will achieve message integrity
                byte[] content = await request.Content.ReadAsByteArrayAsync();
                MD5 md5 = MD5.Create();
                byte[] requestContentHash = md5.ComputeHash(content);
                requestContentBase64String = Convert.ToBase64String(requestContentHash);
            }
            //Creating the raw signature string by combining
            //APPId, request Http Method, request Uri, request TimeStamp, nonce, request Content Base64 String
            string signatureRawData = String.Format("{0}{1}{2}{3}{4}{5}", APPId, requestHttpMethod, requestUri, requestTimeStamp, nonce, requestContentBase64String);
            //Converting the APIKey into byte array
            var secretKeyByteArray = Convert.FromBase64String(APIKey);
            //Converting the signatureRawData into byte array
            byte[] signature = Encoding.UTF8.GetBytes(signatureRawData);
            //Generate the hmac signature and set it in the Authorization header
            using (HMACSHA256 hmac = new HMACSHA256(secretKeyByteArray))
            {
                byte[] signatureBytes = hmac.ComputeHash(signature);
                string requestSignatureBase64String = Convert.ToBase64String(signatureBytes);
                //Setting the values in the Authorization header using custom scheme (hmacauth)
                request.Headers.Authorization = new AuthenticationHeaderValue("hmacauth", string.Format("{0}:{1}:{2}:{3}", APPId, requestSignatureBase64String, nonce, requestTimeStamp));
            }
            response = await base.SendAsync(request, cancellationToken);
            return response;
        }
    }
}
Explanation of the above code:
In the above example, for the simplicity of this demo and to focus of HMAC Authentication, we have hardcoded the APP Id and API Key values which we already obtained from the server, but in real-time, we need to store those values in some secure repository like a database or in the config file. The following code does this.
What is HMAC Authentication in WEB API?

We have got the full request URI and then we safely Encoded the URL. We need to do this because if there are any query strings sent with the request, then they will be safely encoded. Then we read the HTTP Method type from the request object, in our case, the HTTP Method type will be POST. The following code shows this
Why we need HMAC Authentication in WEB API?

Then we have calculated the time stamp value for the request using the UNIX timing (number of seconds since Jan. 1st 1970). This will help us to avoid any issues that might happen if the client and the server reside in two different time zones. The following code does this.
What are the Uses of HMAC Authentication in C#?

Next, we have generated a nonce value by using GUID and this value needs to be unique for each request. The following line of code does the same.
How does the HMAC Authentication work?

Then we have checked whether the request contains a body or not. If the request type is POST or PUT, then it will contain a body. In our example the request type is POST, so it contains a body. If the request contains a body then we need to do the following things;

First, we need to use any hashing algorithm to hash the body content. Here we are using the MD5 hashing algorithm to hash the body content. Once the hash is generated, then we need to convert that hash into a Base64 string. The following code does the same.
How to implement HMAC Authentication in ASP.NET Web API?

Then, we need to build the signature raw data by combining the parameters such as APP Id, request HTTP Method Type, request URI, request timestamp, nonce, and requestContentBase64String without any delimiters. The following code does this.
How to implement HMAC Authentication in C#?

Then we need to convert the signature raw data into a byte array. The following code does the same.

byte[] signature = Encoding.UTF8.GetBytes(signatureRawData);

Finally, we have applied the hashing algorithm using the shared secret API Key and convert the result into the base64 format and combined the (APPId:requestSignatureBase64String:nonce:requestTimeStamp) using ‘:’ colon delimiter and set this combined string in the Authorization header for the request using a custom scheme named “hmacauth”. The following code does the same thing.
ASP.NET Web API with HMAC Authentication

Notice that the nonce and the timestamp values are also included in creating the request signature as well as they are sent as plain text values so that they can be validated on the server to protect our API from replay attacks. In the latter part of this article, we will discuss what are reply attacks and how to avoid them?

That’s it. We have done the implementation of the client application. Now let’s move to build the Web API application (server) which will be protected using the HMAC Authentication.

Section3: Building the Server (Backend API)
As a Web Server, here we are going to create a Web API application which will be protected using the HMAC Authentication. Let’s discuss the step by step procedure to implement this.

Step1: Add an empty Web API Application
Create an empty Web API application with the name “HMACAuthenticationWebApi” as shown in the image below.
HMAC Authentication in C#

Step2: Adding Order Model
Create a class file within the Models folder with the name Order which will exactly the same Orders class that we created in the Client application. So copy and paste the following code within Order.cs file
using System;
using System.Collections.Generic;
namespace HMACAuthenticationWebApi.Models
{
    public class Order
    {
        public int OrderID { get; set; }
        public string CustomerName { get; set; }
        public string CustomerAddress { get; set; }
        public string ContactNumber { get; set; }
        public Boolean IsShipped { get; set; }
        public static List<Order> GetOrders()
        {
            List<Order> OrderList = new List<Order>
            {
                new Order {OrderID = 101, CustomerName = "Pranaya", CustomerAddress = "Amman", ContactNumber = "9876543210", IsShipped = true },
                new Order {OrderID = 102, CustomerName = "Anurag", CustomerAddress = "Dubai",ContactNumber = "9876543210", IsShipped = false},
                new Order {OrderID = 103, CustomerName = "Priyanka", CustomerAddress = "Jeddah", ContactNumber = "9876543210", IsShipped = false },
                new Order {OrderID = 104, CustomerName = "Hina", CustomerAddress = "Abu Dhabi",ContactNumber = "9876543210", IsShipped = false},
                new Order {OrderID = 104, CustomerName = "Sambit", CustomerAddress = "Kuwait", ContactNumber = "9876543210",IsShipped = true}
            };
            return OrderList;
        }
    }
}
The above Order class implementation is very simple and straightforward. We have one method which will return the list of orders.

Step3: Adding Orders Web API Controller
Here we will add an empty Web API Controller with the name “Orders” within the Controllers Folder and then we will create two simple HTTP methods. So, create the Orders Controller and then copy and paste the following code in it.
using HMACAuthenticationWebApi.Models;
using System.Net.Http;
using System.Security.Claims;
using System.Web.Http;
namespace HMACAuthenticationWebApi.Controllers
{
    [RoutePrefix("api/Orders")]
    public class OrdersController : ApiController
    {
        [Route("")]
        public IHttpActionResult Get()
        {
            return Ok(Order.GetOrders());
        }
        [Route("")]
        public IHttpActionResult Post(Order order)
        {
            return Ok(order);
        }
    }
}
In the above Web API Controller, we are not doing anything special, just a basic Web API controller which is not protected and allows anonymous calls. Later we will discuss how to protect this.

Step4: Build the HMAC Authentication Filter
Here we will add a Custom Authentication Filter (a class which is inherited from the IAuthenticationFilter) and within that Custom Filter, we need to write the logic to re-generating the HMAC signature from the request data and Authorization header and then we also need to write the logic compares the generated HAMC Signature with the signature received from the client.

The Authentication Filter is available from Web API 2 and we need to use this filter only for authentication purposes. In our case, we will use this custom Authentication Filter to write the validation logic which will validate the authenticity of the signature received from the client.

The important thing about this filter is that it runs before any other filters especially the authorization filter runs. Create a class file with the name HMACAuthenticationAttribute within the Models folder and then copy and paste the following code.

Note: You will get a compiler error for IsValidRequest and ResultWithChallenge method that we will implement in our next step.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Caching;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Filters;
using System.Web.Http.Results;
namespace HMACAuthenticationWebApi.Models
{
    public class HMACAuthenticationAttribute : Attribute, IAuthenticationFilter
    {
        private static Dictionary<string, string> allowedApps = new Dictionary<string, string>();
        private readonly UInt64 requestMaxAgeInSeconds = 300; //Means 5 min
        private readonly string authenticationScheme = "hmacauth";
        public HMACAuthenticationAttribute()
        {
            if (allowedApps.Count == 0)
            {
                allowedApps.Add("65d3a4f0-0239-404c-8394-21b94ff50604", "WLUEWeL3so2hdHhHM5ZYnvzsOUBzSGH4+T3EgrQ91KI=");
            }
        }
        public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
        {
            var req = context.Request;
            if (req.Headers.Authorization != null && authenticationScheme.Equals(req.Headers.Authorization.Scheme, StringComparison.OrdinalIgnoreCase))
            {
                var rawAuthzHeader = req.Headers.Authorization.Parameter;
                var autherizationHeaderArray = GetAutherizationHeaderValues(rawAuthzHeader);
                if (autherizationHeaderArray != null)
                {
                    var APPId = autherizationHeaderArray[0];
                    var incomingBase64Signature = autherizationHeaderArray[1];
                    var nonce = autherizationHeaderArray[2];
                    var requestTimeStamp = autherizationHeaderArray[3];
                    var isValid = IsValidRequest(req, APPId, incomingBase64Signature, nonce, requestTimeStamp);
                    if (isValid.Result)
                    {
                        var currentPrincipal = new GenericPrincipal(new GenericIdentity(APPId), null);
                        context.Principal = currentPrincipal;
                    }
                    else
                    {
                        context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
                    }
                }
                else
                {
                    context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
                }
            }
            else
            {
                context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
            }
            return Task.FromResult(0);
        }
        public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
        {
            context.Result = new ResultWithChallenge(context.Result);
            return Task.FromResult(0);
        }
        public bool AllowMultiple
        {
            get { return false; }
        }
        private string[] GetAutherizationHeaderValues(string rawAuthzHeader)
        {
            var credArray = rawAuthzHeader.Split(':');
            if (credArray.Length == 4)
            {
                return credArray;
            }
            else
            {
                return null;
            }
        }
    }
}
Explanation of the Above Code:
The above HMACAuthenticationAttribute class is derived from the Attribute class. So we can use this HMACAuthenticationAttribute class as a Filter Attribute over the controllers or HTTP action methods.

The constructor of the HMACAuthenticationAttribute class currently filled a dictionary named “allowedApps”. We did this only for the demo purpose. In real-time, we need to store the Public Shared APP Id and Private Secret API Key in some secure repository like a database or config file.

In the “AuthenticateAsync” method, we implement the logic for validating the signature of the incoming request.

Next, we need to make sure that the Authorization Header is present in the request and it should not be empty. We also need to make sure that it contains the “hmacauth” scheme. If everything is fine, then we need to read the Authorization Header value from the request and then split its content based on the delimiter we have specified earlier in client i.e. using a colon “:”.

Finally, we are calling the “IsValidRequest” method where we implement all the logic of reconstructing the HMAC Signature from the request data and then comparing this signature with the incoming signature. We will implement this method in Step6 of this section.

In case the Authorization Header is not present or if the Authorization Header does not the “hmacauth” scheme or if the “IsValidRequest” method returns false, then we will consider this request as an unauthorized request and returns 401 unauthorized. We should return an authentication challenge to the response, and this should be implemented within the method “ChallengeAsync” which we will implement in the next step.

Step5: Adding the authentication challenge to the response
To add the authentication challenge to the unauthorized response, create a class file with the name ResultWithChallenge within the Models folder and then copy and paste the following code.
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
namespace HMACAuthenticationWebApi.Models
{
    public class ResultWithChallenge : IHttpActionResult
    {
        private readonly string authenticationScheme = "hmacauth";
        private readonly IHttpActionResult next;
        public ResultWithChallenge(IHttpActionResult next)
        {
            this.next = next;
        }
        public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            var response = await next.ExecuteAsync(cancellationToken);
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(authenticationScheme));
            }
            return response;
        }
    }
}
The above code is very simple. Basically here we add the “WWW-Authenticate” header to the response using our “hmacauth” custom scheme.

Step6: Implementing the IsValidRequest Method
The custom implementation logic of reconstructing the signature and comparing it with the signature received from the client is done here. So let’s add the code first and then we will discuss what this method is responsible for. Open the file “HMACAuthenticationAttribute.cs” which is present inside the Models Folder and then paste the following code below the GetAutherizationHeaderValues method.
private async Task<bool> IsValidRequest(HttpRequestMessage req, string APPId, string incomingBase64Signature, string nonce, string requestTimeStamp)
{
    string requestContentBase64String = "";
    string requestUri = HttpUtility.UrlEncode(req.RequestUri.AbsoluteUri.ToLower());
    string requestHttpMethod = req.Method.Method;
    if (!allowedApps.ContainsKey(APPId))
    {
        return false;
    }
    var sharedKey = allowedApps[APPId];
    if (isReplayRequest(nonce, requestTimeStamp))
    {
        return false;
    }
    byte[] hash = await ComputeHash(req.Content);
    if (hash != null)
    {
        requestContentBase64String = Convert.ToBase64String(hash);
    }
    string data = String.Format("{0}{1}{2}{3}{4}{5}", APPId, requestHttpMethod, requestUri, requestTimeStamp, nonce, requestContentBase64String);
    var secretKeyBytes = Convert.FromBase64String(sharedKey);
    byte[] signature = Encoding.UTF8.GetBytes(data);
    using (HMACSHA256 hmac = new HMACSHA256(secretKeyBytes))
    {
        byte[] signatureBytes = hmac.ComputeHash(signature);
        return (incomingBase64Signature.Equals(Convert.ToBase64String(signatureBytes), StringComparison.Ordinal));
    }
}
private bool isReplayRequest(string nonce, string requestTimeStamp)
{
    if (System.Runtime.Caching.MemoryCache.Default.Contains(nonce))
    {
        return true;
    }
    DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
    TimeSpan currentTs = DateTime.UtcNow - epochStart;
    var serverTotalSeconds = Convert.ToUInt64(currentTs.TotalSeconds);
    var requestTotalSeconds = Convert.ToUInt64(requestTimeStamp);
    if ((serverTotalSeconds - requestTotalSeconds) > requestMaxAgeInSeconds)
    {
        return true;
    }
    System.Runtime.Caching.MemoryCache.Default.Add(nonce, requestTimeStamp, DateTimeOffset.UtcNow.AddSeconds(requestMaxAgeInSeconds));
    return false;
}
private static async Task<byte[]> ComputeHash(HttpContent httpContent)
{
    using (MD5 md5 = MD5.Create())
    {
        byte[] hash = null;
        var content = await httpContent.ReadAsByteArrayAsync();
        if (content.Length != 0)
        {
            hash = md5.ComputeHash(content);
        }
        return hash;
    }
}
Explanation of the Above Code:
First, we check whether the received Public Shared APP ID is registered in our system or not, if it is not registered in our system, then we simply return false from the isValidRequest method. But if we found the Public Shared APP ID in our system, then we need to check whether the request received is a replay request.

What is Replay Request?
The replay request means we need to check if the nonce received from the client is used before. Currently, we are storing all the nonce received from the client in Cache Memory for 5 minutes only. Here we are using the Runtime Caching.

Let us understand the replay request with an example. For example, if the client generates a nonce lets say “abcd1234” and send it with the request to the server. Then the server will check whether this nonce “abcd1234” is used before. If not then the server will store the nonce value in Cache Memory for the next 5 minutes. So any request coming from the client with the same nonce value i.e. “abcd1234” during the 5 minutes time interval will be considered as a replay attack or replay request. if the same nonce “abcd1234” is used after 5 minutes time interval then this is fine and the request is not considered as a replay request.

But there might a situation where let’s say an evil person might try to re-post the same request using the same nonce after the 5 minutes window, so in a situation like this the request timestamp becomes handy, the implementation here is comparing the current server UNIX time with the request UNIX time received from the client, if the request timestamp is older than 5 minutes then it is rejected by the server and the evil person has no possibility to fake the request timestamp and send fresher one because we have already included the request timestamp in the signature raw data, so any changes on it will result in a new signature and it will not match the client incoming signature.

In the last step, we need to compute the MD5 hash of the body content if it is available (POST or PUT methods), then we built the signature raw data by concatenating the parameters (APP ID, request HTTP method, request URI, request timestamp, nonce, requestContentBase64String) without any delimiters. It is mandatory that both the parties (Client and Server) need to use the same data format to produce the same signature; the data eventually will get hashed using the same hashing algorithm and API Key used by the client. If the incoming client signature equals the signature generated on the server then we will consider this request as authentic and will process it.

Step7: Securing the API End Points:
The final thing here we need to do is to add the HMACAuthentication attribute to the controller actions so that the action is protected from the anonymous access. So open the Orders controller and add the attribute “HMACAuthentication” as shown below:
using HMACAuthenticationWebApi.Models;
using System.Web.Http;
namespace HMACAuthenticationWebApi.Controllers
{
    [RoutePrefix("api/Orders")]
    public class OrdersController : ApiController
    {
        [Route("")]
        [HMACAuthentication]
        public IHttpActionResult Get()
        {
            return Ok(Order.GetOrders());
        }
        [Route("")]
        [HMACAuthentication]
        public IHttpActionResult Post(Order order)
        {
            return Ok(order);
        }
    }
}
That’s it. We have done with our implementation. So first the run the Web API and application and figure out the port number on which the application is running. Once you figure out the Port number on which your API is running, you need to update the above port number with API base address port number in the Client Application. Once you update the Port number, then run the client application and see everything is working as expected as shown in the below image.
HMAC Authentication in WEB API

In this article, we discussed the HMAC authentication with the HTTP Request and in the next article, we will discuss how to use the HMAC authentication with the HTTP Response.

In this article, I try to explain how to implement HMAC Authentication in the WEB API step by step with an example. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.

Summary:
I hope this post will be helpful to understand the concept of HMAC Authentication in Web API
Please share this post with your friends and colleagues.
For any queries please post a comment below.
Happy Coding 😉
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg

Consume Refresh Token in C# Client

 Admin     July 26, 2020     .Net, Asp.Net, C#, Web API     No comments   

In this article, I will discuss how to Consume Refresh Token in C# Client application. Please read the following three articles, before proceeding to this article as we are going to consume the services that we created in our previous articles.

Token-Based Authentication in Web API: In this article, we discussed how to implement and use the Token-Based Authentication in Web API.

Client Validation in Token Based Authentication: In this article, we discussed how to validate the clients while generating the token in Web API.

Generating Refresh Token in Web API: In this article, we discussed how to Generate Refresh Token in Web API.

Let us discuss the step by step procedure to Consume Refresh Token in C#. But before that let’s modify the Test Controller of our Web API application that we created in our previous application as shown below.

Step1: Modifying the Test Controller
using System.Linq;
using System.Security.Claims;
using System.Web.Http;
namespace TokenAuthenticationInWebAPI.Controllers
{
    public class TestController : ApiController
    {
        //This resource is For all types of role
        [Authorize(Roles = "SuperAdmin, Admin, User")]
        [HttpGet]
        [Route("api/test/resource1")]
        public IHttpActionResult GetResource1()
        {
            var identity = (ClaimsIdentity)User.Identity;
            
            return Ok("Hello: " + identity.Name);
        }
        
        //This resource is only For Admin and SuperAdmin role
        [Authorize(Roles = "SuperAdmin, Admin")]
        [HttpGet]
        [Route("api/test/resource2")]
        public IHttpActionResult GetResource2()
        {
            var identity = (ClaimsIdentity)User.Identity;            
            var Email = identity.Claims
                      .FirstOrDefault(c => c.Type == "Email").Value;
            var UserName = identity.Name;
            
            return Ok("Hello " + UserName + ", Your Email ID is :" + Email);
        }
        //This resource is only For SuperAdmin role
        [Authorize(Roles = "SuperAdmin")]
        [HttpGet]
        [Route("api/test/resource3")]
        public IHttpActionResult GetResource3()
        {
            var identity = (ClaimsIdentity)User.Identity;
            var roles = identity.Claims
                        .Where(c => c.Type == ClaimTypes.Role)
                        .Select(c => c.Value);
            return Ok("Hello " + identity.Name + "Your Role(s) are: " + string.Join(",", roles.ToList()));
        }
    }
}
Step2: Creating the UserTokenMaster table
In the client-side, we need to store the token in the UserTokenMaster table as shown below
Consume Refresh Token in C#

Please use the below SQL Script to create the required database.
CREATE DATABASE Test_DB
GO
USE Test_DB
CREATE TABLE UserTokenMaster
(
  UserName VARCHAR(50) PRIMARY KEY,
  UserPassword VARCHAR(50),
  AccessToken VARCHAR(500),
  RefreshToken VARCHAR(100),
  TokenExpiredTime DATETIME
)
GO
Step3: Create a new console application with the name RefreshTokenClient1.

Step4: Add ADO.NET Entity Data Model
Here, we need to add ADO.NET Entity Data Model Database First approach against the Test_DB and add the UserTokenMaster to the EDMX that we created in Step2. So once you add the table, the EDMX File should be as shown below
Consume Refresh Token in C#

Step5: Add Token class
Now we need to add a class file with the name Token to the project. And then copy and paste the following code.
using Newtonsoft.Json;
using System;
namespace RefreshTokenClient1
{
    // The body of the response from API is a JSON object that 
    // contains the following properties (and a couple of others
    // that we're not capturing).
    public class Token
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }
        [JsonProperty("token_type")]
        public string TokenType { get; set; }
        [JsonProperty("expires_in")]
        public int ExpiresIn { get; set; }
        [JsonProperty("refresh_token")]
        public string RefreshToken { get; set; }
        
        public string Error { get; set; }
        public DateTime ExpiredDateTime { get; set; }
    }
}
Step6: Adding UserTokenRepository
Now we need to add a class with the name UserTokenRepository and within that class, we are going to perform the database operations. So once you add the class, copy and paste the following code.
using System;
using System.Linq;
namespace RefreshTokenClient1
{
    class UserTokenRepository : IDisposable
    {
        // Test_DBEntities it is your context class
        Test_DBEntities context = new Test_DBEntities();
        public Token GetTokenFromDB(string username, string password)
        {
            UserTokenMaster userMaster = context.UserTokenMasters.FirstOrDefault(user =>
            user.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)
            && user.UserPassword == password);
            Token token = null;
            if (userMaster != null)
            {
                token = new Token()
                {
                    AccessToken = userMaster.AccessToken,
                    RefreshToken = userMaster.RefreshToken,
                    ExpiredDateTime = (DateTime)userMaster.TokenExpiredTime
                };
            }
            return token;
        }
        //Adding Token into the DB
        public bool AddUserTokenIntoDB(UserTokenMaster userTokenMaster)
        {
            //First Check the existance of the Token in the DB
            var tokenMaster = context.UserTokenMasters.FirstOrDefault(x => x.UserName == userTokenMaster.UserName
                            && x.UserPassword == userTokenMaster.UserPassword);
            if (tokenMaster != null)
            {
                context.UserTokenMasters.Remove(tokenMaster);
            }
            context.UserTokenMasters.Add(userTokenMaster);
            bool isAdded = context.SaveChanges() > 0;
            return isAdded;
        }
        
        public void Dispose()
        {
            context.Dispose();
        }
    }
}
Step7: Modify the Program class
Here we need to implement the logic to get the access token and refresh from token API and then storing the Token into our database.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
namespace RefreshTokenClient1 {
 class Program {
  // When your application is registered you will get
  // the client id and secret from the API
  private static string _clientId = “DOTNET”;
  private static string _clientSecret = “EEF47D9A - DBA9 - 4D02 - B7B0 - 04F4279A6D20”;
  private static string Username = “”;
  private static string Password = “”;
  //Store the base address of the web api
  //You need to change the PORT number where your WEB API service is running
  private static string baseAddress = “http: //localhost:65061/”;
  static void Main(string[] args) {
   Token token = null;
   Username = “Anurag”;
   Password = “123456”;
   // First get the token from the persistent storage based
   // on the username and password
   token = (new UserTokenRepository()).GetTokenFromDB(Username, Password);
   //Then check the existing token and its expiration datetime
   if (token != null && DateTime.Now < token.ExpiredDateTime) {
    //use the existing token
   }
   else if (token != null && !string.IsNullOrEmpty(token.RefreshToken)) {
    //Get a new access token based on the Refresh Token
    token = GetTokens(_clientId, _clientSecret, token.RefreshToken);
   }
   else {
    //Get a brand new access token
    token = GetTokens(_clientId, _clientSecret, null);
   }
   //If you get the access token, then call the authorized resource
   if (!string.IsNullOrEmpty(token.AccessToken)) {
    CallAPIResource1(token.AccessToken);
   }
   else {
    Console.WriteLine(token.Error);
   }
   Console.ReadLine();
  }
  //Here we implment the logic to call the authorized resource
  private static void CallAPIResource1(string AccessToken) {
   HttpClientHandler handler = new HttpClientHandler();
   HttpClient client = new HttpClient(handler);
   // Need to set the Access Token in the Authorization Header as shown below
   client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Bearer”, AccessToken);
   // Make a Get request for the authorized resource by invoking
   // the PostAsync method on the client object as shown below
   var APIResponse = client.GetAsync(baseAddress + “api / test / resource1”).Result;
   if (APIResponse.IsSuccessStatusCode) {
    var JsonContent = APIResponse.Content.ReadAsStringAsync().Result;
    string Message = JsonConvert.DeserializeObject<string>(JsonContent);
    Console.WriteLine(“APIResponse: ” + Message);
   }
   else {
    Console.WriteLine(“APIResponse, Error: ” + APIResponse.StatusCode);
   }
  }
  //In this method we need to implement the logic whether we need get a brand new access token
  // or we need the access token based on the Refresh Token
  private static Token GetTokens(string clientId, string clientSecret, string RefreshToken) {
   Token token = null;
   if (string.IsNullOrEmpty(RefreshToken)) {
    token = GetAccessToken(clientId, clientSecret, Username, Password);
   }
   else {
    token = GetAccessTokenByRefreshToken(clientId, clientSecret, RefreshToken);
    // The Refresh token can become invalid for several reasons
    // such as invalid cliendid and secret or the user’s password has changed.
    // In Such cases issue a brand new access token
    if (!string.IsNullOrEmpty(token.Error)) {
     token = GetAccessToken(clientId, clientSecret, Username, Password);
    }
   }
   if (!string.IsNullOrEmpty(token.Error)) {
    throw new Exception(token.Error);
   }
   else {
    //Need to store the token in any presistent storage
    token.ExpiredDateTime = DateTime.Now.AddSeconds(token.ExpiresIn);
    //Create an object of type UserTokenMaster
    UserTokenMaster userTokenMaster = new UserTokenMaster() {
     UserName = Username,
     UserPassword = Password,
     AccessToken = token.AccessToken,
     RefreshToken = token.RefreshToken,
     TokenExpiredTime = token.ExpiredDateTime
    };
    bool IsAddeded = (new UserTokenRepository()).AddUserTokenIntoDB(userTokenMaster);
    if (IsAddeded) {
     token.Error = “Error Occurred
     while saving the Token into the DB”;
    }
   }
   return token;
  }
  //This method is used to get a new access token
  public static Token GetAccessToken(string clientId, string clientSecret, string username, string password) {
   Token token = new Token();
   HttpClientHandler handler = new HttpClientHandler();
   HttpClient client = new HttpClient(handler);
   // Need to set the Client ID and Client Secret in the Authorization Header
   // in Base64 Encoded Format using the Basic Authentication as shown below
   string ClientIDandSecret = clientId + “: ” + clientSecret;
   var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientIDandSecret));
   client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Basic”, authorizationHeader);
   // Create a dictionary which contains the request form data, here we need to set
   // the username, password and grant_type as shown below
   var RequestBody = new Dictionary<string, string> {
    {“grant_type”,
     “password”
    }, {“username”, username
    }, {“password”, password
    },
   };
   //Make a Post request by invoking the PostAsync method on the client object as shown below
   var tokenResponse = client.PostAsync(baseAddress + “token”, new FormUrlEncodedContent(RequestBody)).Result;
   if (tokenResponse.IsSuccessStatusCode) {
    var JsonContent = tokenResponse.Content.ReadAsStringAsync().Result;
    token = JsonConvert.DeserializeObject<Token>(JsonContent);
    token.Error = null;
   }
   else {
    token.Error = “GetAccessToken failed likely due to an invalid client ID,
    client secret,
    or invalid usrename and password”;
   }
   return token;
  }
  //This method is used to get a new access token based on the Refresh Token
  public static Token GetAccessTokenByRefreshToken(string clientId, string clientSecret, string refreshToken) {
   Token token = new Token();
   HttpClientHandler handler = new HttpClientHandler();
   HttpClient client = new HttpClient(handler);
   // Need to set the Client ID and Client Secret in the Authorization Header
   // in Base64 Encoded Format using Basic Authentication as shown below
   string ClientIDandSecret = clientId + “: ” + clientSecret;
   var authorizationHeader = Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientIDandSecret));
   client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(“Basic”, authorizationHeader);
   // Create a dictionary which contains the refresh token, here we need to set
   // the grant_type as refresh_token as shown below
   var RequestBody = new Dictionary<string, string> {
    {“grant_type”,
     “refresh_token”
    }, {“refresh_token”, refreshToken
    }
   };
   //Make a Post request by invoking the PostAsync method on the client object as shown below
   var tokenResponse = client.PostAsync(baseAddress + “token”, new FormUrlEncodedContent(RequestBody)).Result;
   if (tokenResponse.IsSuccessStatusCode) {
    var JsonContent = tokenResponse.Content.ReadAsStringAsync().Result;
    token = JsonConvert.DeserializeObject<Token>(JsonContent);
    token.Error = null;
   }
   else {
    token.Error = “GetAccessToken by Refresh Token failed likely due to an invalid client ID,
    client secret,
    or it has been revoked by the system admin”;
   }
   return token;
  }
 }
}
That’s it. Run the application and see everything is working as expected. In the next article, I will discuss how to consume Refresh Token using JavaScript.

In this article, I try to explain how to Consume Refresh Token in C# Client Application with an example. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.

Summary:
I hope this post will be helpful to understand how to Consume Refresh Token in C# Client
Please share this post with your friends and colleagues.
For any queries please post a comment below.
Happy Coding 😉
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg

Refresh Token in Web API

 Admin     July 26, 2020     .Net, Asp.Net, C#, Web API     1 comment   

In this article, I am going to discuss how to implement Refresh Token in Web API by validating the clients, as well as I, will also discuss how to persist the refresh token into a database. Please read the following two articles before proceeding to this article as we are going to use the same example that we worked in our previous two articles.

Token Based Authentication in Web API: In this article, we discussed how to implement and use the Token Based Authentication in Web API.

Client Validation in Token Based Authentication: In this article, we discussed how to validate the clients while generating the token in Web API.

What is a Refresh Token?
A Refresh Token is a special kind of token that can be used to obtain a new renewed access token which allows access to the protected resources. You can request for the new access tokens by using the Refresh Token in Web API until the Refresh Token is blacklisted.

Why we need Refresh Token in Web API?
The idea of using the refresh token is to issue a short-lived access token (up to 30 minutes) for the first time and then use the refresh token to obtain a new access token and use that access token to access the protected resources.

So, the user needs to provide the username and password along with the client info (i.e. the client id and client secret) to authenticate himself, and if the information provided by the user is valid, then a response contains a short-lived access token along with a long-lived refresh token gets generated.

The refresh token is not an access token it is just an identifier for the access token. Now once the access token is expired, the user can use the refresh token to obtain another short-lived access token and so on.

Why not long-lived access token?
Now, you may have one question in your mind. Why not we are issuing a long-lived access token for the first time?

Let’s discuss why not a long-lived access token or what are the advantages of using refresh token in Web API. Mainly there are three main reasons to use the refresh tokens are as follows

Updating the Access Token Content:
As we already discussed, the access tokens are self-contained tokens means they contain all the information (which is known as claims) of an authenticated user once the access token is generated.

Now, if we issue a long-lived access token, let say for example 1 month, for a user let’s say “Anurag” and let say the user “Anurag” is enrolled with the role “Users” at the moment. So all this information gets stored on the access token which is generated by the Authorization Server.

If you have decided (3 days after he obtained the access token) to add him with the role “Admin” and then there is no way to update this information in the access token which is already generated, you need to ask him to re-authenticate himself again, so that the Authorization server add the updated information to the newly generated access token, and this is not feasible in most of the cases. You might not be able to reach to the users who already obtained the long-lived access tokens.

So to overcome the above issue, you need to issue short-lived access token (30 minutes for example) along with a long-lived refresh token and then the user needs to use the refresh token to obtain the newly updated access token, once the user obtains the new access token, the Authorization Server will be able to add the updated claims or new claims to the new access token being generated.

Revoking the Access from Authenticated users:
Once the user obtained the long-lived access token, then he will be able to access the server resources as long as his access token is not expired and there is no standard way to revoke the access tokens unless and until the Authorization Server implements some custom logic to store the generated access token a database and need to do database checks with each and every request.

But with the refresh token, a database or system admin can simply revoke the access by deleting the refresh token identifier from the database. So, when the user requests a new access token by using the deleted refresh token, the Authorization Server will reject this request because the refresh token is no longer available in the database.

No need to store or ask for the username and password frequently:
Using refresh token allows you to ask the user for his username and password only one time (i.e. for the first time), then the Authorization Server can issue very long-lived refresh token (1 year for example) and the user will stay logged in all this period until and unless system admin tries to revoke (delete) the refresh token. This can be very useful if you are building an API that will be consumed by a front-end application where it is not feasible to keep asking for the username/password frequently.

So for the above three major reasons we need to use Refresh Tokens.

The Refresh Tokens and Clients In order to use the refresh token, we need to be bound to the refresh token with a Client. In simple words, we can define a client as an application who wants to access our resources. Each Client should have a unique Client Id and Client Secret.

The Client Id is a piece of unique public information that identifies the application among other applications. The client id can be included in the source code of your application, but the client secret must stay confidential.

Bounding the refresh token to a client is very important this is because you do not want any refresh token generated by your Authorization Server to be used by another client to obtain the access token.

The schema for the client’s table should be as shown below.
Refresh Token in Web API

In our previous article, we create the ClientMaster table, so let’s delete the existing ClientMaster table and regenerate the ClientMaster table with the above structure.

Please use below SQL Script to DROP, Create and Populate the ClientMaster table with two different clients which we are going to use in this demo.
USE [SECURITY_DB]
GO
--First DROP ClientMaster table
DROP TABLE ClientMaster
-- Create the ClientMaster table with new structure
CREATE TABLE [ClientMaster](
  [ClientKeyId] INT PRIMARY KEY IDENTITY(1,1),
  [ClientID] VARCHAR(500) NOT NULL,
  [ClientSecret] VARCHAR(500) NOT NULL,
  [ClientName] VARCHAR(100) NOT NULL,
  [Active] BIT NOT NULL,
  [RefreshTokenLifeTime] INT NOT NULL,
  [AllowedOrigin] VARCHAR(500) NOT NULL
)
GO
-- INSERT the client details inti the ClientMaster table
INSERT INTO ClientMaster VALUES('DOTNET',NEWID(),'MyClient1',1,7200,'*')
INSERT INTO ClientMaster VALUES('Tutorials',NEWID(),'Dot Net Tutorials',1,14400,'https://dotnettutorials.net')
GO
Let’s us discuss the use of each column of ClientMaster table
The ClientID and ClientSecret columns of the ClientMaster table uniquely identify a particular client.

The Active column is also very important; if the system admin is decided to deactivate a particular client so that any new requests asking for the access token from that particular deactivated client will be rejected by the Authorization Server.

The Refresh Token Life Time column is used to set when the refresh token (not the access token) will expire in minutes.

Finally, the Allowed Origin column is used to configure the CORS and to set “Access-Control-Allow-Origin” on the back-end API.

Refresh Token Schema: As we already discussed, we need to store the refresh tokens generated by the Authorization Server into a database and this is very important to facilitate the management for refresh tokens. The schema for the Refresh Token table as shown in the below image:
Refresh Token in Web API

Please use below SQL Script to create the RefershToken table.
-- Create the RefreshToken table
CREATE TABLE RefreshToken
(
  [ID] VARCHAR(500) PRIMARY KEY,
  [UserName] VARCHAR(500),
  [ClientID] VARCHAR(500),
  [IssuedTime] DATETIME,
  [ExpiredTime] DATETIME,
  [ProtectedTicket] VARCHAR(500)
)
Let discuss the use of each column of the RefreshToken table.
The ID column of the RefreshToken table contains the hashed value of the refresh token id, the API consumer will receive and send the plain refresh token Id and the UserName column indicates to which user this refresh token belongs, and the same thing is applied for ClientID column indicating that the Token belongs to that particular clients.

By having the ClientID column, as a system admin, you can revoke (delete) the refresh token for a certain user on a certain client and keep the other refresh tokens for the same user obtained by different clients. For example, let say you have two clients having the same username, if you delete one user for a particular client, then the same user of other clients can access the refresh token.

The IssuedTime and ExpiredTime columns are for display purposes only.

Finally, the Protected Ticket column contains the magical signed string which contains a serialized representation for the ticket for a specific user, in other words, it contains all the claims and ticket properties for a user.

We have discussed enough theory, so it’s time to put all the theories into practice. So let’s discuss the step by step procedure to implement the Refresh Token in Web API. As I already told you that we are going to use the same example that we worked with our previous two articles.

Step1: Modify the EDMX file
We need to modify the EDMX file to add the newly generated RefreshToken table and we also need to update the ClientMaster table. Once you modify your EDMX file, the EDMX file should look as shown below.
Refresh Token in Web API

Step2: Modify the ClientMasterRepository class as shown below
namespace TokenAuthenticationInWebAPI.Models
{
    public class ClientMasterRepository : IDisposable
    {
        // SECURITY_DBEntities it is your context class
        SECURITY_DBEntities context = new SECURITY_DBEntities();
        //This method is used to check and validate the Client credentials
        public ClientMaster ValidateClient(string ClientID, string ClientSecret)
        {
            return context.ClientMasters.FirstOrDefault(user =>
             user.ClientID == ClientID
            && user.ClientSecret == ClientSecret);
        }
        public void Dispose()
        {
            context.Dispose();
        }
    }
}
Step3: Adding a Helper class
Add a class file with the name Helper.cs and then copy and paste the following code
using System;
using System.Security.Cryptography;
namespace TokenAuthenticationInWebAPI.Models
{
    public class Helper
    {
        public static string GetHash(string input)
        {
            HashAlgorithm hashAlgorithm = new SHA256CryptoServiceProvider();
            byte[] byteValue = System.Text.Encoding.UTF8.GetBytes(input);
            byte[] byteHash = hashAlgorithm.ComputeHash(byteValue);
            return Convert.ToBase64String(byteHash);
        }
    }
}
The above GetHash method is straightforward; it takes an input of string type and returns its hash value.

Step4: Add AuthenticationRepository class file
Add a class file with the name AuthenticationRepository.cs and then copy and paste the following code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TokenAuthenticationInWebAPI.Models
{
    public class AuthenticationRepository : IDisposable
    {
        SECURITY_DBEntities context = new SECURITY_DBEntities();
        //Add the Refresh token
        public async Task<bool> AddRefreshToken(RefreshToken token)
        {
            var existingToken = context.RefreshTokens.FirstOrDefault(r => r.UserName == token.UserName 
                            && r.ClientID == token.ClientID);
            if (existingToken != null)
            {
                var result = await RemoveRefreshToken(existingToken);
            }
            context.RefreshTokens.Add(token);
            return await context.SaveChangesAsync() > 0;
        }
        //Remove the Refesh Token by id
        public async Task<bool> RemoveRefreshTokenByID(string refreshTokenId)
        {
            var refreshToken = await context.RefreshTokens.FindAsync(refreshTokenId);
            if (refreshToken != null)
            {
                context.RefreshTokens.Remove(refreshToken);
                return await context.SaveChangesAsync() > 0;
            }
            return false;
        }
        //Remove the Refresh Token
        public async Task<bool> RemoveRefreshToken(RefreshToken refreshToken)
        {
            context.RefreshTokens.Remove(refreshToken);
            return await context.SaveChangesAsync() > 0;
        }
        //Find the Refresh Token by token ID
        public async Task<refreshtoken> FindRefreshToken(string refreshTokenId)
        {
            var refreshToken = await context.RefreshTokens.FindAsync(refreshTokenId);
            return refreshToken;
        }
        //Get All Refresh Tokens
        public List<refreshtoken> GetAllRefreshTokens()
        {
            return context.RefreshTokens.ToList();
        }
        public void Dispose()
        {
            context.Dispose();
        }
    }
}
The methods we add in the above AuthenticationRepository class will add support for manipulating the RefreshToken table that we have added, they are self-explanatory methods and there is nothing special about them.

Step5: Modify the Client Validation logic
Here, we need to modify the logic responsible for validating the client information whether the request needs an access token or uses a refresh token to obtain a new access token. To modify the ValidateClientAuthentication method of the MyAuthorizationServerProvider class as shown below.
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
    string clientId = string.Empty;
    string clientSecret = string.Empty;
    // The TryGetBasicCredentials method checks the Authorization header and
    // Return the ClientId and clientSecret
    if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
    {
        context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
        return Task.FromResult<object>(null);
    }
    //Check the existence of by calling the ValidateClient method
    ClientMaster client = (new ClientMasterRepository()).ValidateClient(clientId, clientSecret);
    if (client == null)
    {
        // Client could not be validated.
        context.SetError("invalid_client", "Client credentials are invalid.");
        return Task.FromResult<object>(null);
    }
    else
    {
        if (!client.Active)
        {
            context.SetError("invalid_client", "Client is inactive.");
            return Task.FromResult<object>(null);
        }
        // Client has been verified.
        context.OwinContext.Set<clientmaster>("ta:client", client);
        context.OwinContext.Set<string>("ta:clientAllowedOrigin", client.AllowedOrigin);
        context.OwinContext.Set<string>("ta:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
        context.Validated();
        return Task.FromResult<object>(null);
    }
}
Explanation of the above code
We are trying to get the Client ID and Client Secret from the authorization header using a basic scheme, so the user needs to send the Client ID and Client Secret in the base64 encode format (client_id:client_secret) and need to send it in the Authorization header of the HTTP Request.

Once we receive the client id and client secret, we need to check it in our database whether the client is already registered with our back-end API or not (means we need to validate the client), if it is not registered we will invalidate the context and reject the request.

If the client is registered, then we will check whether the client is active or not, if it is not active, then we will also invalidate the context and reject the request

And, if we found the client is active, then we need to store the client allowed origin and refresh token lifetime value on the Owin context (if you want then you can store all information of the client), so it will be available once we generate the refresh token.

If all is valid we mark the context as a valid context which means that client validation has passed and the flow can proceed to the next step.

Step6: Validating the Resource Owner Credentials
Once the client validation has been passed, next we need to validate the resource owner credentials i.e. the username and password are correct or not, and then we need to bound the client id to the accession generated. To do so, let’s modify the GrantResourceOwnerCredentials method of the MyAuthorizationServerProvider class as shown below.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    ClientMaster client = context.OwinContext.Get<clientmaster>("ta:client");
    var allowedOrigin = context.OwinContext.Get<string>("ta:clientAllowedOrigin");
    if (allowedOrigin == null)
    {
        allowedOrigin = "*";
    }
    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
    UserMaster user = null;
    using (UserMasterRepository _repo = new UserMasterRepository())
    {
        user = _repo.ValidateUser(context.UserName, context.Password);
        if (user == null)
        {
            context.SetError("invalid_grant", "Provided username and password is incorrect");
            return;
        }
    }
    var identity = new ClaimsIdentity(context.Options.AuthenticationType);
    identity.AddClaim(new Claim(ClaimTypes.Role, user.UserRoles));
    identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
    identity.AddClaim(new Claim("Email", user.UserEmailID));
    var props = new AuthenticationProperties(new Dictionary<string string="">
                {
                    {
                        "client_id", (context.ClientId == null) ? string.Empty : context.ClientId
                    },
                    {
                        "userName", context.UserName
                    }
                });
    var ticket = new AuthenticationTicket(identity, props);
    context.Validated(ticket);
}
Explanation of the Above Code:
First, we need to read the client information from the Owin context and then we add the clientAllowedOrigin value to add the header “Access-Control-Allow-Origin” to Owin context response as shown in the below image.
Refresh Token in Web API

Then we will check the username and password for the resource owner and if it is valid, then we will generate the set of claims for the above user along with authentication properties which contain the client id and username as shown in the below image.
Refresh Token in Web API

Now the access token will be generated behind the scenes when we call the context.Validated(ticket) method as shown in the below image.
Refresh Token in Web API

Step8: Implementing the TokenEndpoint method
Now we need to override the TokenEndpoint method within the MyAuthorizationServerProvider class with the following code.
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
    foreach (KeyValuePair<string string=""> property in context.Properties.Dictionary)
    {
        context.AdditionalResponseParameters.Add(property.Key, property.Value);
    }
    return Task.FromResult<object>(null);
}
The above TokenEndpoint method is adding some additional properties to the token response.

Step9: Generating Refresh Token in Web API and persisting it into a database
Now we need to generate the Refresh Token and Store it into our database inside the RefreshToken table. To do so, add a class file with the name RefreshTokenProvider.cs under the Models folder and then copy and paste the following code.
using Microsoft.Owin.Security.Infrastructure;
using System;
using System.Threading.Tasks;
namespace TokenAuthenticationInWebAPI.Models
{
    public class RefreshTokenProvider : IAuthenticationTokenProvider
    {
        public async Task CreateAsync(AuthenticationTokenCreateContext context)
        {
            //Get the client ID from the Ticket properties
            var clientid = context.Ticket.Properties.Dictionary["client_id"];
            if (string.IsNullOrEmpty(clientid))
            {
                return;
            }
            //Generating a Uniqure Refresh Token ID
            var refreshTokenId = Guid.NewGuid().ToString("n");
            using (AuthenticationRepository _repo = new AuthenticationRepository())
            {
                // Getting the Refesh Token Life Time From the Owin Context
                var refreshTokenLifeTime = context.OwinContext.Get<string>("ta:clientRefreshTokenLifeTime");
                //Creating the Refresh Token object
                var token = new RefreshToken()
                {
                    //storing the RefreshTokenId in hash format
                    ID = Helper.GetHash(refreshTokenId),
                    ClientID = clientid,
                    UserName = context.Ticket.Identity.Name,
                    IssuedTime = DateTime.UtcNow,
                    ExpiredTime = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
                };
                //Setting the Issued and Expired time of the Refresh Token
                context.Ticket.Properties.IssuedUtc = token.IssuedTime;
                context.Ticket.Properties.ExpiresUtc = token.ExpiredTime;
                token.ProtectedTicket = context.SerializeTicket();
                var result = await _repo.AddRefreshToken(token);
                if (result)
                {
                    context.SetToken(refreshTokenId);
                }
            }
        }
        public Task ReceiveAsync(AuthenticationTokenReceiveContext context)
        {
            throw new NotImplementedException();
        }
        public void Create(AuthenticationTokenCreateContext context)
        {
            throw new NotImplementedException();
        }
        public void Receive(AuthenticationTokenReceiveContext context)
        {
            throw new NotImplementedException();
        }
    }
}
Explanation of the above code:
As shown above, the class RefreshTokenProvider implements the interface IAuthenticationTokenProvider, and here we need to add our refresh token generation logic inside the method CreateAsync.

Let discuss what we have done inside the CreateAsync method. First, we are getting the client id from the Ticket Properties. The following code does the above things.
Refresh Token in Web API

Next, we are generating a unique identifier for the refresh token, here, I am using Guid which is enough for this, or you can use your own unique string generation algorithm. The following code exactly does the same.
Refresh Token in Web API

Then we are reading the refresh token lifetime value from the Owin context and this value was set when we validate the client, this value will be used to determine how long the refresh token will be valid for, this should be in minutes.

Then we are setting the IssuedUtc, and ExpiresUtc values for the ticket, setting those properties will determine how long the refresh token in web API will be valid for.

After setting all context properties we are calling the context.SerializeTicket() method will be responsible to serialize the ticket content and we will be able to store this magical serialized string on to the database. The following diagram shows the above things.
Refresh Token in Web API

Now we strong the above token record into the RefreshTokens table, note that we are checking the token which will be saved on the database is unique for this Username (User) and the Client, if it not unique first we will delete the existing one and then store the new refresh token. It is better to hash the refresh token identifier before storing it, so if anyone has access to the database he will not be able to see the real refresh tokens.

Finally, we will send back the refresh token id (without hashing it) in the response body. The following does the above thing.
Refresh Token in Web API

Step10: Modifying the Start class (OwinStartup class)
We need to set the RefreshTokenProvider class within the OAuthAuthorizationServerOptions, so open the class Start which is present inside the app_start folder and replace the code used to set OAuthAuthorizationServerOptions, with the below code, you can notice that we are setting the access token lifetime to a short period now (30 minutes) instead of 24 hours.
using System;
using Microsoft.Owin;
using Owin;
using TokenAuthenticationInWebAPI.Models;
using Microsoft.Owin.Security.OAuth;
using System.Web.Http;
[assembly: OwinStartup(typeof(TokenAuthenticationInWebAPI.App_Start.Startup))]
namespace TokenAuthenticationInWebAPI.App_Start
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        { 
            OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
            {
                AllowInsecureHttp = true,
                //The Path For generating the Toekn
                TokenEndpointPath = new PathString("/token"),
                //Setting the Token Expired Time (30 minutes)
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
                //MyAuthorizationServerProvider class will validate the user credentials
                Provider = new MyAuthorizationServerProvider(),
                //For creating the refresh token and regenerate the new access token
                RefreshTokenProvider = new RefreshTokenProvider()
            };
            
            app.UseOAuthAuthorizationServer(options);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
            
            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
        }
    }
}
Step11: Testing the API using Postman:
Let’s first create the Base64 Encode value by for the ClientID and ClientSecret by using the following website

https://www.base64encode.org/

Enter the Client ID and Client Secret separated by a colon (:) in “Encode to Base64 format” textbox, and then click on the “Encode” button as shown in the below diagram which will generate the Base64 encoded value.

Example: ClientID: DOTNET and Client Secret: EEF47D9A-DBA9-4D02-B7B0-04F4279A6D20
Refresh Token in Web API

Base64 Code value: RE9UTkVUOkVFRjQ3RDlBLURCQTktNEQwMi1CN0IwLTA0RjQyNzlBNkQyMA==

Once you generate the Base64 encoded value, let’s see how to use basic authentication in the header to pass the Base64 encoded value. Here we need to use the Authorization header and the value will be the Base64 encoded string followed the “BASIC” as shown below.

Authorization: BASIC RE9UTkVUOkVFRjQ3RDlBLURCQTktNEQwMi1CN0IwLTA0RjQyNzlBNkQyMA==

Let’s see step by step procedure to use the Postman to generate the Access Token

Step1: Select the Method as POST and provide URI as shown below in the below image
Refresh Token in Web API

Step2: Select the Header tab and provide the Authorization value as shown below.

Authorization: BASIC RE9UTkVUOkVFRjQ3RDlBLURCQTktNEQwMi1CN0IwLTA0RjQyNzlBNkQyMA==
Refresh Token in Web API

Step3: Select the Body Tab. Then choose the x-www-form-urlencoded option and provide the username and password value. Provide the grant_type value as a password as shown in the below image.
Refresh Token in Web API

Now click on the Send button which will generate the access token along with the refresh token as shown below.
Refresh Token in Web API

As shown in the response body, you will notice that we have obtained a refresh_token along with the access token which should be used to obtain a new access token (we will discuss this after a while in this post) this token is bounded to the user Anurag and for the Client DOTNET. Note that the expires_in value is related to the access token, not the refresh token, this access token will expire in 30 mins.

Step12: Generating an Access Token using the Refresh Token in Web API
Now we need to implement the logic needed to generate a new access token when we receive the request from the refresh the token, to do so open the class RefreshTokenProvider and implement the ReceiveAsync method as shown below.
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
    var allowedOrigin = context.OwinContext.Get< string>("ta:clientAllowedOrigin");
    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
    string hashedTokenId = Helper.GetHash(context.Token);
    using (AuthenticationRepository _repo = new AuthenticationRepository())
    {
        var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
        if (refreshToken != null)
        {
            //Get protectedTicket from refreshToken class
            context.DeserializeTicket(refreshToken.ProtectedTicket);
            var result = await _repo.RemoveRefreshTokenByID(hashedTokenId);
        }
    }
}
Explanation of the above method:
We need to set the “Access-Control-Allow-Origin” header by getting the value from the Owin Context. If you will not set this value, then you will get 405 status code and this is because the method “GrantResourceOwnerCredentials” where we set this header is never get executed once we request the access token using the refresh tokens (grant_type = refresh_token).

Then we get the refresh token id from the request, hash this id and look for the token using the hashed refresh token id in “RefreshToken” table, if the refresh token is found, we will use the magical signed string which contains a serialized representation for the ticket to building the ticket and identities for the user mapped to this refresh token.

Finally, we will remove the existing refresh token from the “RefreshToken” table because in our logic we are allowing only one refresh token per user and client.

Implementing GrantRefreshToken in Web API
Now the request context contains all the claims stored previously for this use. Now we need to implement the logic which will allows us to issue new claims or updating the existing claims and contain them into the new access token generated before sending it to the user, to do so open class MyAuthorizationServerProvider and implement method GrantRefreshToken with the following code.
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
    var originalClient = context.Ticket.Properties.Dictionary["client_id"];
    var currentClient = context.ClientId;
    if (originalClient != currentClient)
    {
        context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
        return Task.FromResult<object>(null);
    }
    // Change auth ticket for refresh token requests
    var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
    newIdentity.AddClaim(new Claim("newClaim", "newValue"));
    var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
    context.Validated(newTicket);
    return Task.FromResult<object>(null);
}
Explanation of the above code:
First, we are reading the client id value from the original ticket and this is the client ids which get stored in the magical signed string. Then we are comparing this client id with the client id sent with the request, if they are different then we will reject this request because we need to make sure that the refresh token used here is bound to the same client when it was generated.

Now, we have the chance to add new claims or remove or update existing claims, to do this we are calling the “context.Validated(newTicket)” method which will generate the new access token and return it in the response body.

Lastly, after this method executes successfully, the flow for the code will hit the “CreateAsync” method which is present in the class “RefreshTokenProvider” and a new refresh token is generated and returned in the response along with the new access token.

Testing the Refresh Token in Web API with Postman to generate new access Token:
Step1: Select the Method as POST and provide URI as shown below in the below image
Refresh Token in Web API

Step2: Select the Header tab and provide the Authorization value as shown below. This is the Base64 encoded value for the ClientID and Client Secret.

Authorization: BASIC RE9UTkVUOkVFRjQ3RDlBLURCQTktNEQwMi1CN0IwLTA0RjQyNzlBNkQyMA==
Refresh Token in Web API

Step3: Select the Body Tab. Then choose the x-www-form-urlencoded option and provide the Refresh_Token value and the grant_type value as refresh_token as shown in the below image.
Refresh Token in Web API

In the next article, I am going to discuss how to use the Refresh Token in different types of Client Applications. We will discuss how to use it from C# and JavaScript clients. Here, in this article, I try to explain how to implement Refresh Token in Web API with an example. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.

Summary:
I hope this post will be helpful to understand the concept of Refresh Token in Web API
Please share this post with your friends and colleagues.
For any queries please post a comment below.
Happy Coding 😉
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Older Posts

Join us on Telegram

Loved Our Blog Posts? Subscribe To Get Updates Directly To Your Inbox

Like us on Facebook

Popular Posts

  • Recursion And Back Tracking
    In this article, I am going to discuss Recursion And BackTracking in detail. Please read our previous article where we discussed Master Th...
  • Comparison Between HttpModule and HttpContext
    Hi friends! Today we are going to list some of the differences between HttpModule and HttpContext. Many of my developer friends are confus...
  • How to Reverse a String in C#
    In this article, I am going to discuss How to Reverse a String in C# with and without using built-in Methods. Please read our previous art...
  • Reverse Number Program in C# with Examples
    In this article, I am going to discuss the Reverse Number Program in C# with some examples. Please read our previous article where we discu...
  • Armstrong Number Program in C# with Examples
    In this article, I am going to discuss the Armstrong Number Program in C# with some examples. Please read our previous article before proc...
  • Binary to Decimal Conversion in C# with Examples
    In this article, I am going to discuss the Binary to Decimal Conversion in C# with some examples. Please read our previous article where w...
  • Decimal to Binary Conversion in C# with Examples
    In this article, I am going to discuss the Decimal to Binary Conversion in C# with some examples. Please read our previous article where w...

Blog Archive

Contact Form

Name

Email *

Message *

Tags

.Net .Net Core .Net Core MVC Algorithm Angular Anonymous Types Asp.Net Asp.Net MVC Blazor C# Data Structure Database Design Patterns Entity Framework Entity Framework Core Filters Interview Question Management Studio Programming Programs SQL Server SSMS Web API

Copyright © C# Techtics | All Right Reserved.

Protected by Copyscape