• ApiAuthorization.Plugin

ApiAuthorization.Plugin

Assembly: ApiAuthorization.Plugin

Description

There are many different ways in which you can secure an Api endpoint so that the resources are only used by those authorised to use them.

When writing open and accesible Api's that can be used across a public domain it is important to follow some basic principles, these being:

  • Prioritize Security. Think about the type of security that you want to employ for endpoints at the start of a project, do not leave it as an afterthought, or believe that it's somebody elses issue.
  • Use a strong authentication solution. Authenticating a user, using industry standard techniques can help protect data and ensure that only those requiring access to data are able to get access.
  • Use authorization. Just because a user can authenticate, it does not mean the should have carte blanche once authentication is successfull. Using a principal of least privilege can help ensure that a user is not authorized to perform any action unless that action has been specifically granted.

Implementing Api Authorization

Api authorization is accomplished by applying an attribute to the endpoint or controller for all endpoints and implementing two interfaces

  • ApiAuthorizationAttribute. An attribute that is put on each controller or individual action endpoint.
  • IApiAuthorizationService. The application must register an instance of IApiAuthorizationService within the service container. This interface contains one method and is used by the attribute to validate the request.
  • IUserApiQueryProvider. The application must register an instance of IUserApiQueryProvider within the service container.

ApiAuthorizationAttribute

The ApiAuthorization attribute will obtain an instance of IApiAuthorizationService from the registered DI container and call the service to validate the credentials passed in the header for each request.

If no IApiAuthorizationService is registered then the call will fail with a not allowed http response (405)

This attribute can optionally have a policy applied to further tighten security. The following example shows a controller with two endpoints, of which one takes the name of a policy which can alse be verified when calling the IApiAuthorizationService implementation.

[ApiController]
public class DemoApiController : ControllerBase
{
    [ApiAuthorization]
    public IActionResult RequiresAuthorization()
    {
        return new JsonResult(new { response = "If you can see this you passed the security without a policy" });
    }
 
    [ApiAuthorization("AdminPolicy")]
    public IActionResult RequiresAuthorizationUsingPolicy()
    {
        return new JsonResult(new { response = "If you can see this you passed the security with a policy" });
    }
}

IApiAuthorizationService

This interface can utilise any authorization security that the application wants, to apply a custom authorization service, create a new class that descends from IApiAuthorizationService and add the class to the DI container.

The following code is taken from the ApiAuthorization plugin and automatically adds a Hmac implementation that will use hmac validation to authorize endpoints.

public void AfterConfigureServices(in IServiceCollection services)
{
    // if no api validation has been added, add the Hmac validation
    services.TryAddSingleton<IApiAuthorizationService, HmacApiAuthorizationService>();
}

It is important to note that Hmac is one implementation, an application owner is free to implement different types of authentication.

IUserApiQueryProvider

IUserApiQueryProvider is an application supplied concrete implementation that is used to retrieve a client secret given a merchant id and api key. This is required to also be added to the DI service container if using the default Hmac implementation.

Integrating ApiAuthorization.Plugin

Integrating the ApiAuthorization plugin is relatively simple, the following code sample registers the plugin with the PluginManagerService:

public static class Program
{
    public static void Main(string[] args)
    {
        // add plugins which need to be loaded first in a specific order
        PluginManagerService.UsePlugin(typeof(ApiAuthorization.Plugin.PluginInitialisation));
 
        PluginManagerConfiguration configuration = new PluginManagerConfiguration
        {
            ServiceConfigurator = new ServiceConfigurator()
        };
 
        // Initialise the plugin manager service
        PluginManagerService.Initialise(configuration);
        try
        {
            CreateWebHostBuilder(args).Build().Run();
        }
        finally
        {
            PluginManagerService.Finalise();
        }
    }
 
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseContentRoot(System.IO.Directory.GetCurrentDirectory())
            .UseDefaultServiceProvider(options =>
                options.ValidateScopes = false);
}

The next code example registers an IUserApiApiQueryProvider instance:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IUserApiQueryProvider, MockUserApiQueryProvider>();
}

That is all that is required, each endpoint that has the ApiAuthorization attribute will now be protected using Hmac authentication.

HmacGenerator

There is helper class called HmacGenerator that contains helper methods for creating a random nonce value, generating the current time as an epoch time and generating a Hmac given the users merchant id, api key and secret.

Security First

Hmac relies on 3 pieces of data, the merchant id to identify the user, the api key to recognise the application and the api secret which is unique to the api key for the merchant. It is critical these values do not get stored in code but instead are read from a configuration file or other such application storage.