JWT authentication in ASP.NET Core using HttpClient

In my last two articles (you can read them here and here) we discussed how JWT authentication can be implemented in ASP.NET Core APIs and jQuery client. In this article we will use .NET Core's HttpClient component to perform JWT authentication.

If you wish to call the Employee API from server side C# code (say an MVC controller) or a desktop application, you will typically use HttpClient component. The overall process of JWT authentication with HttpClient remains the same. You first grab a JWT token by calling the Security API and then include this token in the authorization header while calling the Employee API.

To illustrate how this works, we will develop a simple application as shown below:

The above page looks quite similar to the jQuery example we developed earlier. However, the big difference is that the three buttons Sign IN, Sign Out, and Show Data cause form submission. The HomeController handles these form submissions through three actions namely Login(), Logout(), and ShowData(). These actions use HttpClient component to call the Security API and the Employee API.

Let's see these three actions one-by-one.

Enable Session state

In the jQuery client example we used sessionStore to store the JWT token received from the Security API. We need similar storage mechanism while working with HttpClient also. One possibility is to use ASP.NET Core Session state. You need to enable it in the ConfigureServices() and Configure() methods as shown below:

public void ConfigureServices(IServiceCollection services)
{
  ...
  services.AddMemoryCache();
  services.AddSession();
}

And

public void Configure(IApplicationBuilder app,
IHostingEnvironment env)
{
  ...

  app.UseSession();

  ...
}

Once Session state is made available to your application, you can store and retrieve the JWT token in it.

Index view

Ok. Now let's see how the Index view housing the three buttons looks like:

<form method="post">
    <button type="submit" asp-action="Login"
asp-controller="Home">Sign In</button>
    <button type="submit" asp-action="Logout"
asp-controller="Home">Sign Out</button>
    <button type="submit" asp-action="ShowData"
asp-controller="Home">Show Data</button>
    <h2>@Html.Raw(@ViewBag.Message)</h2>
</form>

Notice that the form tag helper doesn't specify the controller and action name. That's because each of the buttons is submitting to a different action. Therefore, the button tag helper specifies the asp-action for each button. The Sign In button submits to the Login() action, the Sign Out button submits to the Logout() action, and the Show Data button submits to ShowData() action.

The response from these actions is displayed on the page using Message ViewBag property.

Login() action of HomeController

The Login() action invokes the Security API's Login() action in an attempt to retrieve a JWT token. The token is then stored in the Session. The following code shows how this is done.

public IActionResult Login()
{
    string baseUrl = "http://localhost:49387";
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(baseUrl);
    var contentType = new MediaTypeWithQualityHeaderValue
("application/json");
    client.DefaultRequestHeaders.Accept.Add(contentType);

    User userModel = new User();
    userModel.UserName = "User1";
    userModel.Password = "pass$word";

    string stringData = JsonConvert.SerializeObject(userModel);
    var contentData = new StringContent(stringData,
System.Text.Encoding.UTF8, "application/json");

    HttpResponseMessage response = client.PostAsync
("/api/security",contentData).Result;
    string stringJWT = response.Content.
ReadAsStringAsync().Result;
    JWT jwt = JsonConvert.DeserializeObject
<JWT>(stringJWT);

    HttpContext.Session.SetString("token", jwt.Token);

    ViewBag.Message = "User logged in successfully!";

    return View("Index");
}

The above code creates an instance of HttpClient component and sets its BaseAddress and content type header. Make sure to change the base address as per your environment. The code then creates a User object and stores UserName and Password in it. This User object needs to be sent to the Security API for authentication purpose.

In order to send User object to the Security API we convert it into StringContent object and then invoke PostAsync() method. The PostAsync() method of HttpClient makes a POST request to the Security API and carries User credentials along with it.

Recollect that Login() action of Security API is returning Ok() along with JWT token. This response is read using ReadAsStringAsync() method and de-serialized into JWT object. The JWT class looks like this:

public class JWT
{
    public string Token { get; set; }
}

Then the code stores the JWT token into Session using SetString() method. A success message is returned to the client.

Logout() action of HomeController

The Logout() action simply removes the JWT token stored in the Session.

public IActionResult Logout()
{
    HttpContext.Session.Remove("token");
    ViewBag.Message = "User logged out successfully!";
    return View("Index");
}

A message is displayed to indicate successful logging out operation.

ShowData() action of HomeController

The ShowData() action is where we call the Employee API. While calling the Employee API we need to send the JWT token received earlier in the Authorization header. The following code shows how it is done.

public IActionResult ShowData()
{
    string baseUrl = "http://localhost:49387";
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri(baseUrl);
    var contentType = new MediaTypeWithQualityHeaderValue
("application/json");
    client.DefaultRequestHeaders.Accept.Add(contentType);

    client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer",
HttpContext.Session.GetString("token"));

    HttpResponseMessage response = client.GetAsync
("/api/employee").Result;
    string stringData = response.Content.
ReadAsStringAsync().Result;
    List<Employee> data = JsonConvert.DeserializeObject
<List<Employee>>(stringData);

    if (response.StatusCode == HttpStatusCode.Unauthorized)
    {
        ViewBag.Message = "Unauthorized!";
    }
    else
    {
        string strTable = "<table border='1' cellpadding='10'>";
        foreach (Employee emp in data)
        {
            strTable += "<tr>";
            strTable += "<td>";
            strTable += emp.EmployeeID;
            strTable += "</td>";
            strTable += "<td>";
            strTable += emp.FirstName;
            strTable += "</td>";
            strTable += "<td>";
            strTable += emp.LastName;
            strTable += "</td>";
            strTable += "</tr>";

        }
        strTable += "</table>";

        ViewBag.Message = strTable;
    }

    return View("Index");
}

The above code creates HttpClient object as before. It then adds the Authorization header using DefaultRequestHeaders.Authorization property. The scheme parameter of AuthorizationHeaderValue is set to Bearer and the JWT token stored in the Session is passed as its second parameter.

Then we call GetAsync() in an attempt to receive the employee data. If HttpResponseMessage returns a StatusCode of 401 (as indicated by HttpStatusCode.Unauthorized enum value) it indicates that the authentication has failed. And hence we display an error message. 

If authentication has succeeded, we iterate through the employee List and form <table> markup with EmployeeID, FirstName, and LastName. This table is then displayed on the page as shown in the first figure of this article.

That's it for now! Keep coding!!


Bipin Joshi is a software consultant, trainer, author, yoga mentor, and spiritual guide having 24+ years of experience in software development, consulting, and training. He conducts instructor-led online training courses in ASP.NET Core, ASP.NET MVC, and Design Patterns for individuals and small groups. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get article updates : Facebook  Twitter  LinkedIn

Posted On : 11 February 2019


Tags : ASP.NET ASP.NET Core MVC C# Visual Studio


Subscribe to our newsletter

Get monthly email updates about new articles, tutorials, code samples, and how-tos getting added to our knowledge base.

  

Receive Weekly Updates