Do you know Azure Function have Function-Filters?

I probably think that you are not aware of Function-Filters in Azure function as I was. Recently I was working on Azure functions API based project and was thinking what if we had similar functionality of ASP.NET MVC Filters in Azure function as well it will help us solve the problem of customizing or request pipeline and help common logic to shared across Azure functions.
If you want to see how Function Filters can help us solve some real life problems, then this article is for you.

If you are an .NET developer you would have implemented ASP.NET MVC Filters in your Web applications.

In this article we will see how Function-Filters are very similar to ASP.NET Filters and we will implement them  in HTTP Trigger Azure function  to solve real life problem and discuss the pron and cons of using it.

Special For You! Are you getting started as an Azure .NET developer and are not aware from where to start and when to use which service? Have you already checked out my Azure Book for .NET Developers ? It’s a full basic learning book getting started with Microsoft Azure Step by Step in 7 days for .NET Developers and many bonuses included source code and access to other Azure Topics.   Click here to check it out!.

Before Diving in:

I hope you are aware of Azure Function and ASP.NET MVC Filters in order to understand this article. If not I would suggest you to read this really awesome article written by Nitin Pandit.

I hope by now you will be having at least basic knowledge of MVC Filter so that you should be able to understand Function-Filters in Azure Functions. Here is a Figure demonstrating  MVC filters and implementation.

MVC Filters are used to execute some pre-processing and post-processing logic.

Examples

  1. Authorization Filter: Checking whether Logged In user is valid or not?
  2. Exception Filter attribute whenever there is an error raised by controller and you want to execute your logic after that. etc.
  3. Custom Filters are Filters are attributes created by you for your own custom requirement for ex: To check permission of Logged In User

So now the questions comes what kind of Filters are available in Function-Filters.

  1. FunctionInvocationFilterAttribute
  2. FunctionExceptionFilterAttribute

FunctionInvocationFilterAttribute: Filter used to execute PRE and POST processing logic when target job function is invoked.

FunctionExceptionFilterAttribute: Exception Filter will be called whenever there is an exception thrown by the Azure Function.

Function-Filters can be applied globally to all functions, to a Class level or to a Function level.

Prerequisite:

  1. Visual Studio 2017 with Cloud SDKs installed

Lets now create a HTTP Trigger Azure function using Visual Studio 2017

HTTP Trigger Template Code:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.Azure.WebJobs.Host;
using System.Threading;

namespace FunctionFilters
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public  async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string name = req.Query["name"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;

            return name != null
                ? (ActionResult)new OkObjectResult($"Hello, {name}")
                : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
        }

        
    }
}

Now in order to implement Function-Filter in our Function class, We first need to implement Function-Filters  from

IFunctionExceptionFilter and IFunctionInvocationFilter.

We have to change our Class from static to instance Function and implement both of these Function Filters at Class level and override their respective method OnExceptionAsync, OnExecutedAsync and OnExecutingAsync:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace FunctionFilters
{
    public  class Function1:IFunctionExceptionFilter, IFunctionInvocationFilter
    {
        [FunctionName("Function1")]
        public  async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {

            Debug.WriteLine("C# HTTP trigger function processed a request.");

            string name = req.Query["name"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;

            return name != null
                ? (ActionResult)new OkObjectResult($"Hello, {name}")
                : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
        }

        public Task OnExceptionAsync(FunctionExceptionContext exceptionContext, CancellationToken cancellationToken)
        {
            Debug.WriteLine($"Exception raised by the application {exceptionContext.Exception.ToString()}");
            return Task.CompletedTask;
        }

        public Task OnExecutedAsync(FunctionExecutedContext executedContext, CancellationToken cancellationToken)
        {
            Debug.WriteLine($"I should be executed at last");
            return Task.CompletedTask;
        }

        public Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken)
        {
            Debug.WriteLine($"I should be executed at before function code");
            return Task.CompletedTask;
        }
    }
}

You can see the implemented Function Filters  look quiet similar to ASP.NET MVC Filter.

  • OnExceptionAsync:  Filter falls under category of invocation function filter which will get executed whenever there will be any exception occurred in our Function.
  • OnExecutingAsync: Filter falls under category of invocation function which will get  executed before our function logic is executed and this help us executing any kind of pre-processing logic.
  • OnExecutedAsync: Filter falls under category of Exception Filters filter gets executed after our function logic is executed and this helps us executing any kind post-processing logic.

Lets run function filter by executing them in our local environment. In order to test HTTP trigger Azure function I will be using Postman which is an HTTP Rest client.

Demo of OnExecutingAsync and OnExecutedAsync Function Filters

Demo of OnExceptionAsync Filter

In order to test the OnExceptionAsync, I am throwing an DivideByZeroException in our code so that function will crash and our OnExceptionAsync filter will be invoked.

Testing the function with Postman and check the order of the Function Filters

 

Life Cycle of Function Filters

Now we are aware of Function Filter in Azure Function, Now you will be thinking where I can use Function Filters in practical real life scenarios.

We can use Function Filters in below some of the Real Life Scenarios

  • Generic Exception Handlers for all Azure Functions
  • Sharing common logic across all HTTP Trigger Azure Function

Case Study: Enterprise Application Architecture of API Management and HTTP Trigger Azure functions 

From the above architecture we can see the Authorization is happening at Azure Function level. So you can imagine that we would have written duplicate code of checking the Authorization bearer token in each Azure function and now we will try to move that code to Authorize Function Filter.

In order to share a common logic across all HTTP trigger Azure function, I want to create a Generic Authorization Filter for all of my HTTP Azure function to check the HTTP header for JWT token and If the request headers doesn’t contain Authorization bearer token we will reject the request with Unauthorized. These seems to be like a real life scenario because I want to keep my Authorization Logic code in Filter and reuse it all the Azure functions which should be accessed by valid users.

In an Enterprise Application normally we have API Management as middle man who forwards only valid request to the Azure function. API management has many benefits that you can read from this awesome Benefits of using Azure API Management with micro-services.

In our Implementation we will be using HTTP triggered Azure functions but no API management service. We will create an Authorization Function Filter to check the JWT token in each request and if user sends the Invalid JWT token we will return Unauthorized Response status to the User.

Let’s get started and create our HTTP Trigger Azure function using Visual studio and name it as per your preference:

Select HTTP Trigger Template and select Azure Functions V1 because in version V2 I had some issues with HTTP trigger function when i tested on my local machine while writing this.

Create a new Class  in our solution and name it as FunctionAuthorizeAttribute :

Implement FunctionInvocationFilterAttribute  and override only OnExecutingAsync method.

Create a ValidationPackage Class which will store the properties of JWT token when extracted. If you are new to JWT Token concept my honest suggestion will be go and learn basic of JWT from here and get your JWT token for testing.

You can extract the JWT token from JWT website.

 

 

public class ValidationPackage
    {
        public bool ValidToken { get; set; }
        public string PrincipalName { get; set; }
        public string Scope { get; set; }
        public string AppID { get; set; }
        public long IssuedAt { get; set; }
        public long ExpiresAt { get; set; }
        public string Token { get; set; }
        public string LastName { get; internal set; }
        public string FirstName { get; internal set; }
        public ValidationPackage()
        {
            ValidToken = false;
        }
    }

FunctionAuthorizeAttribute code:

Now in OnExecutingAsync function we will be writing our logic to read Bearer token from request header and extract claims out of the token and validate it. If token is valid we will add a new Header to the request to set whether token is valid or not because as per current implementation we can’t short circuit the pipeline and sent response from Function filters.

In this scenario we are just extracting JWT token not validating if it is registered to our product or not. Make sure you validate the identity as well so that any other JWT token passed will not execute the Azure function.

If the token is valid we are adding a header AuthorizationStatus to the request that stores the HttpStatus code whether it is Accepted or Unauthorized.

 public class FunctionAuthorizeAttribute : FunctionInvocationFilterAttribute
    {
        public FunctionAuthorizeAttribute()
        {
        }

        public override Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken)
        {
            var workItem = executingContext.Arguments.First().Value as HttpRequestMessage;
            ValidationPackage validationPackage = new ValidationPackage();
            AuthenticationHeaderValue jwtInput = workItem.Headers.Authorization;

            if (jwtInput != null)
            {
                String jwt = "";
                if (jwtInput.ToString().StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                {
                    jwt = jwtInput.ToString().Substring("Bearer ".Length).Trim();
                }

                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();

                  try
                    {
                        validationPackage = ExtractClaims(jwt, handler);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                  }               
            }

            if(!validationPackage.ValidToken)
            {
                workItem.Headers.Add("AuthorizationStatus",  Convert.ToInt32(HttpStatusCode.Unauthorized).ToString());
            }
            else
            {
                workItem.Headers.Add("AuthorizationStatus", Convert.ToInt32(HttpStatusCode.Accepted).ToString());          
            }
            return base.OnExecutingAsync(executingContext, cancellationToken);
        }
        public static ValidationPackage ExtractClaims(string jwt, JwtSecurityTokenHandler handler)
        {
            ValidationPackage validationPackage = new ValidationPackage();

            validationPackage.Token = jwt;

            var token = handler.ReadJwtToken(jwt);
            validationPackage.Scope = "user_impersonation";

            try
            {
               
                var claims = token.Claims;
                foreach (Claim c in claims)
                {
                    switch (c.Type)
                    {
                        case "sub":
                        case "upn":
                            if (c.Value.Contains('@'))
                                validationPackage.PrincipalName = c.Value;
                            break;

                        case "Firstname":
                            validationPackage.FirstName = c.Value;
                            break;

                        case "Lastname":
                            validationPackage.LastName = c.Value;
                            break;

                        case "client_id":
                        case "aud":
                            validationPackage.AppID = c.Value;
                            break;

                        case "iat":
                            validationPackage.IssuedAt = Convert.ToInt64(c.Value);
                            break;

                        case "exp":
                            validationPackage.ExpiresAt = Convert.ToInt64(c.Value);
                            break;

                        case "scp":
                            validationPackage.Scope = c.Value;
                            break;
                    }
                }
            }
            catch (Exception e)
            {
                validationPackage.ValidToken = false;
            }
            var currentTimestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;

            if ((validationPackage.ExpiresAt - currentTimestamp) > 0)
                validationPackage.ValidToken = true;
            return validationPackage;
        }

    }

Now we need to add the Function Filter Class as an Attribute to our Http Triggered Azure function as shown in below code snippet.

 public static class GetCustomerById
    {
        [FunctionName("GetCustomerById")]
        [FunctionAuthorize]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestMessage req,
            ILogger log)
        {

            string authorizationStatus = req.Headers.GetValues("AuthorizationStatus").FirstOrDefault();         
            if (Convert.ToInt32(authorizationStatus).Equals((int)HttpStatusCode.Accepted))
            {
                log.LogInformation("C# HTTP trigger function processed a request.");
                var customers = Builder<Customer>
                .CreateListOfSize(10)
                .All().Build();    
                int Id = 1;

                var customer = customers.Where(x => x.Id == Id);
                return customer != null
                    ? (ActionResult)new OkObjectResult(customer)
                    : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
            }
            else
            {
                return new UnauthorizedResult();
            }
        }

    }

You can see that we have added [FunctionAuthorize] to our Function and we are validation and we checking the request headers to check if request had valid JWT Token or not?

string authorizationStatus = req.Headers.GetValues("AuthorizationStatus").FirstOrDefault();         
            if (Convert.ToInt32(authorizationStatus).Equals((int)HttpStatusCode.Accepted))
            {
            } 
            else

            {
                return new UnauthorizedResult();
            }

Lets now run our function app test it without JWT token and check if it working as per our expectation or not?


Testing it with Valid JWT Token

Copy the token from AuthO website.

 

Add the JWT Token to the request header as shown below and then press Send.

Wow, You can see that we are able to Authenticate the JWT token with the help of Function Filter. We can reuse the same logic across all other HTTP trigger azure function without duplicating the logic and keeping the Authorization logic separate.

So we learned how we can leverage the power of Function Filters in Azure function.

But there is a down side of using Function Filters as of now because they are not matured enough and cannot short circuit the request back to the User as we saw we are kind of adding a request header and than checking the header.

While developing you will also see deprecated or obsolete message on the Function filters and that might oppose you from using it in production. I had the same question so I raised it with Azure function team and below is there reply. You can check the whole discussion out here.

But please note the implementation is subjective to change in near future so be aware of it.

If you want to learn Azure from start, my suggestion will be to watch this Learn Azure Step by Step video.

I hope you enjoyed the article as I did writing this up. It’s so cool to know Function Filters and how they help us share common logic across Azure Functions.

If you did, leave your thoughts in the comments below.

Also, I will love it if you share the article on your preferred social media.

References:

Function Filters 

Validation Token JWT

 

One thought on “Do you know Azure Function have Function-Filters?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.