Azure Functions Middleware - Part 2 ( Authentication Middleware)

In Part1 of this series I have briefly explain about middleware and what is next step in that direction. In this post will try to cover one simple middleware. This middleware is for bearer authentication. My intention over here is to explain middleware and how authentication perform so will not go much detail in authentication process itself. Following image specially highlight section that cover as part of authentication. One assumption I made is that, authentication process only perform for HttpTrigger function.

image.png

Following is the flow in simple text.

  1. Request is arrived at Azure function. It receive by first middleware and that is authentication middleware.
  2. Authentication middleware look for authorization header. If header not present then directly 401 status code return. If header present then it check for validation of header or say token. If token is valid then request pass to authorization middleware or any next middleware if configured and if not valid then return 401.

Let's start.

This is how simple middleware look like.

public class SimpleMiddleware :
    IFunctionsWorkerMiddleware
{
    public Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        //TODO: logic goes here.
        return next(context);
    }
}

It needs to configure in host configure.

public class Program
{
    public static void Main()
    {
        var host = new HostBuilder()
            .ConfigureFunctionsWorkerDefaults(configure=>
            {
                // other middleware also configure following way.
                // It will execute in same order it is configured over here.
                configure.UseMiddleware<SimpleMiddleware>();
            })
            .Build();

        host.Run();
    }
}

Following is BearerAutheticationMiddleware

public class BearerAuthenticationMiddleware
        : IFunctionsWorkerMiddleware
    {
        private readonly ILogger<BearerAuthenticationMiddleware> logger;

        public BearerAuthenticationMiddleware(ILogger<BearerAuthenticationMiddleware> logger)
        {
            this.logger = logger;
        }
        public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
        {
            if (context.IsHttpTriggerFunction())
            {
                var headers = context.BindingContext.BindingData["Headers"]?.ToString();
                var httpHeaders = System.Text.Json.JsonSerializer.Deserialize<HttpHeaders>(headers);
                if (httpHeaders?.Authorization != null &&
                    httpHeaders.Authorization.StartsWith("Bearer"))
                {
                    //Validation logic for your token. Here If Bearer present I consider as Valid.
                    if (httpHeaders.Authorization.Contains("admin"))
                    {
                        // Originally based on token get user role.
                        // Put into context.Items so it will be used by next middleware.
                        context.Items.Add("UserRole", "Admin");
                    }
                    await next(context);
                }
                else
                {
                    await context.CreateJsonResponse(System.Net.HttpStatusCode.Unauthorized, new { Message = "Token is not valid." });
                }
            }
            else
            {
                await next(context);
            }
        }
    }

This is how it configured.

public static void Main()
    {
        var host = new HostBuilder()
            .ConfigureFunctionsWorkerDefaults(configure=>
            {
                configure.UseMiddleware<BearerAuthenticationMiddleware>();
            })
            .Build();

        host.Run();
    }

Entire example present here .