Common features in ASP.NET Core 2.1 WebApi: Authentication with a JWT

 

Introduction

Authentication and authorization are required to restrict access to custom and / or sensitive data.
A safer technique than cookie protection is to use tokens, provided by a tier provider.
In this article we will talk about JWT with OpenID Connect.

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner. OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and session management, when it makes sense for them.

For more information about how to configure an Open Id Connect provider you can check this sample with Azure AD here.

Configuring Startup.cs for authentication

Step 1: Download Microsoft.AspNetCore.Authentication.JwtBearer Nuget package

PM> Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -Version 2.1.2

Step 2: Set authentication scheme for JWT

public void ConfigureServices(IServiceCollection services)
{
   // Authentication
   services.AddAuthentication(options =>
   {
      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
      options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
   });
}

Step 3: Set JWT provider configuration

public void ConfigureServices(IServiceCollection services)
{
   // Authentication
   services.AddAuthentication(options =>
   {
      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
      options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
   }).AddJwtBearer(options =>
   {
      options.Authority = "provider end point";
      options.Audience = "application id or uri as identifier";
      options.TokenValidationParameters.ValidateLifetime = true;
      options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
   });
}

Audience represents the intended recipient of the incoming token or the resource that the token grants access to. If the value specified in this parameter doesn’t match the aud parameter in the token, the token will be rejected because it was meant to be used for accessing a different resource. Note that different security token providers have different behaviors regarding what is used as the ‘aud’ claim (some use the URI of a resource a user wants to access, others use scope names). Be sure to use an audience that makes sense given the tokens you plan to accept.

Authority is the address of the token-issuing authentication server. The JWT bearer authentication middleware will use this URI to find and retrieve the public key that can be used to validate the token’s signature. It will also confirm that the iss parameter in the token matches this URI.

ValidateLifetime validates the token expiracy.

ClockSkew allows a certain amount of clock drift. I recommend 5 minutes or less.

Step 4: Set autorization policies

services.AddAuthorization(opts =>
{
   opts.AddPolicy("SurveyCreator", p =>
   {
      // Using value text for demo show, else use enum : ClaimTypes.Role
      p.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "SurveyCreator");
   });
});

This policy require a role claim type with value “SurveyCreator”

Step 5: Protect your actions with Authorize Attribute

[Authorize(Policy = "SurveyCreator")]
[HttpPost]
public void Post([FromBody] string value)
{
}

Remember that if you don’t provide a toekn to your Api or you provide a wrong or expired token the answer will be Http Unauthorized.

If your token doesn’t satisfy the policy, you will get Http Forbidden.

Conclusion

this was an example of implementing security in a web API 🙂

The complete source code can be found here.

How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core?

Introduction

A few years ago, Microsoft introduced the HttpClient class as a modern substitute for HttpWebRequest to make web requests from .NET applications. Not only is this new API much easier to use, cleaner, and asynchronous, but it is also easily expandable.

The HttpClient class has a constructor that accepts a HttpMessageHandler.
The latter is an object that accepts a request (HttpRequestMessage) and returns a response (HttpResponseMessage); the way it does it is completely dependent on the implementation. By default, HttpClient uses HttpClientHandler, a handler that sends a request to a server on the network and returns the response from the server. In this article we will create our own implementation of an HttpMessageHandler by inheriting an abstract class named DelegatingHandler.

Finally, for all this to be possible,  HttpClient must not be used directly, but used with the dependency injection that allow mocking by using IHttpClientFactory interface.

Let’s create a fake HttpMessageHandler

For this example we will talk only about HttpResponseMessage, I won’t treat HttpRequestMessage.

All along this article I will demonstrate how to unit test a class by “mocking” the Http Response.

Here is a sample of a fake  HttpMessageHandler:

public class FakeHttpMessageHandler : DelegatingHandler
{
   private HttpResponseMessage _fakeResponse;

   public FakeHttpMessageHandler(HttpResponseMessage responseMessage)
   {
      _fakeResponse = responseMessage;
   }

   protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   {
      return await Task.FromResult(_fakeResponse);
   }
}

I just override SendAsync and code my own implementation method because I inherit from an abstract class named DelegatingHandler as we talked in introduction.

Once done we just need to add a constructor that accept in parameter a HttpResponseMessage. That’s it ! 🙂

Let’s write a service that uses IHttpClientFactory interface

We will build a service that requests a list of user over a http call to a remote resource:

public class UserService
{
   private readonly IHttpClientFactory _httpFactory;

   public UserService(IHttpClientFactory httpFactory)
   {
      _httpFactory = httpFactory;
   }

   public async Task<List<User>> GetUsers(string url)
   {
       using (HttpClient httpclient = _httpFactory.CreateClient())
       using (HttpResponseMessage response = await httpclient.GetAsync(url))
       {
          if (response.StatusCode == HttpStatusCode.OK)
          {
             List<User> users = await response.Content.ReadAsAsync<List<User>>();
             return users;
          }
          return null; 
       }
    }
}

User contract:

public class User
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
}

As you can see, using IHttpClientFactory allows us to mock the HttpClient instanciation

Let’s test this!

In these unit tests we will use XUnitFluentAssertion and NSubstitute

Scenario 1: let’s mock a call that return two users

public class UserServiceTests
{
   [Fact]
    public async Task WhenACorrectUrlIsProvided_ServiceShouldReturnAlistOfUsers()
    {
       // Arrange
       var users = new List<User>
       {
          new User
          {
             FirstName = "John",
             LastName = "Doe"
          },
          new User
          {
             FirstName = "John",
             LastName = "Deere"
          }
       };

       var httpClientFactoryMock = Substitute.For<IHttpClientFactory>();
       var url = "http://good.uri";
       var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() {
          StatusCode = HttpStatusCode.OK,
          Content = new StringContent(JsonConvert.SerializeObject(users), Encoding.UTF8, "application/json") 
       });
       var fakeHttpClient = new HttpClient(fakeHttpMessageHandler);

       httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);

       // Act
       var service = new UserService(httpClientFactoryMock);
       var result = await service.GetUsers(url);

      // Assert
      result
      .Should()
      .BeOfType<List<User>>()
      .And
      .HaveCount(2)
      .And
      .Contain(x => x.FirstName == "John")
      .And
      .Contain(x => x.LastName == "Deere")
      .And
      .Contain(x => x.LastName == "Doe");
    }
}

As you can see, we expect an HttpStatusCode to “OK” and two users (set in the content).

We expect data in JSON format in the service : List<User> users = await response.Content.ReadAsAsync<List<User>>();

That’s why we need to set the encoding and the media type, else, by default it’s considered as text/plain: new StringContent(JsonConvert.SerializeObject(users), Encoding.UTF8, “application/json”)

We instantiate the HttpClient with the fake handler and then we define our expectation for the mock : httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);

To finish we just need to make our assertions and we are done 🙂

Scenario 2: let’s mock a call that uses a bad Url an return Http 404 and null data

public class UserServiceTests
{
   [Fact]
    public async Task WhenABadUrlIsProvided_ServiceShouldReturnNull()
    {
       // Arrange
       var httpClientFactoryMock = Substitute.For<IHttpClientFactory>();
       var url = "http://bad.uri";
       var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() {
          StatusCode = HttpStatusCode.NotFound
       });
       var fakeHttpClient = new HttpClient(fakeHttpMessageHandler);

       httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);

       // Act
       var service = new UserService(httpClientFactoryMock);
       var result = await service.GetUsers(url);

      // Assert
      result
      .Should()
      .BeNullOrEmpty();
    }
}

Like the service is implemented, if there is an http not found, the result set is null, simply! 😉

You are now able to unit test your classes that make http calls wuth HttpClient easily.

Hope this article helped you 🙂

ASP.NET Core 2.1 middlewares part 1: building a custom middleware

 

Introduction

As part of an ASP.NET Core 2.1 application, middleware makes up the processing chain (pipeline) of HTTP requests. They interact on the response and the HTTP request.

Each middleware can call the following middleware. They can perform operations before AND after their successor.

It is important to understand that the HTTP response is built only when the request is passed in this middleware suite.

This series of articles aims to describe how to build custom middlewares and how to unit test them.

So in a typical ASP.NET Core Startup, we simply use the framework default middlewares:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDeveloperExceptionPage();
   }
   app.UseMvc(); 
}

How to create a middleware

Creating a middleware is really very simple. The IApplicationBuilder interface provides three methods for doing this.

  • Run
  • Map
  • Use

I will just describe in this article the Use method because it allows to create a class we will “use” as a middleware, instead of Run and Map that require an inline declaration.

First, the class must contain an Invoke method that takes an HttpContext parameter and returns a Task. In addition, our class must have a constructor with at least one parameter to use the next middleware (the next method). This one is of type RequestDelegate. However, the constructor may have other parameters. These parameters will be passed to him when adding the middleware in the HTTP pipeline. This addition is done using the UseMiddleware method.

Example with a custom middleware that handle errors:

public class CustomExceptionMiddleware
{
   private readonly RequestDelegate _next;

   public CustomExceptionMiddleware(RequestDelegate next)
   {
      _next = next;
   }

   public async Task Invoke(HttpContext context)
   {
      try
      {
         await _next.Invoke(context);
      }
      catch (Exception ex)
      {
         await HandleExceptionAsync(context, ex);
      }
   }

   private async Task HandleExceptionAsync(HttpContext context, Exception exception)
   {
      var response = context.Response;
      var customException = exception as BaseCustomException;
      var statusCode = (int)HttpStatusCode.InternalServerError;
      var message = "Unexpected error";
      var description = "Unexpected error";

      if (null != customException)
      {
         message = customException.Message;
         description = customException.Description;
         statusCode = customException.Code;
      }

      response.ContentType = "application/json";
      response.StatusCode = statusCode;
      await response.WriteAsync(JsonConvert.SerializeObject(new CustomErrorResponse
      {
         Message = message,
         Description = description
      }));
   }
}

This middleware handle all errors and control the answer to the client: it sends a custom error message.

There are 2 cases :

First case: it’s a custom error that we know what to do with and what to send to the client BaseCustomException

Second case: it’s an unmanaged error that we don’t know what to do with, we will send a generic error message to the client.

Here is the complete implementation of custom errors, DTO and exception raising:

public class BaseCustomException : Exception
{
   private int _code;
   private string _description;

   public int Code
   {
      get => _code;
   }
   public string Description
   {
      get => _description;
   }

   public BaseCustomException(string message, string description, int code) : base(message)
   {
      _code = code;
      _description = description;
   }
}
public class CustomErrorResponse
{
   public string Message { get; set; }
   public string Description { get; set; }
}
public class NotFoundCustomException : BaseCustomException
{
   public NotFoundCustomException(string message, string description) : base(message, description, (int)HttpStatusCode.NotFound)
   {
   }
}
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
   [HttpGet]
   public ActionResult<IEnumerable<string>> Get()
   {
      throw new Exception();
      return new string[] { "value1", "value2" };
   }

   [HttpGet("{id}")]
   public ActionResult<string> Get(int id)
   {
      throw new NotFoundCustomException("No data found", $"Please check your parameters id: {id}");
      return "value";
   }
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDeveloperExceptionPage();
   }
   app.UseMiddleware<CustomExceptionMiddleware>();
   app.UseMvc();
}

Demo

Case 1: Custom error scenario

Case 2: Unhandled error scenario

Fabulous isn’t it ? 😉

Now let’s go to part 2