HTTP Trigger Azure Function Authorization Using Custom Binding

HTTP Trigger Azure Functions, lets you invoke a function with an HTTP request. You can use an HTTP trigger to build serverless APIs and respond to webhooks. Azure provides different types of Authorization levels to secure HTTP functions are like:

  • Function
  • System
  • Anonymous

Function and System, level Authorization can be handled using function and master keys provided by azure, you can learn more on official docs.
In cases where we would like to call the function from a web application like a single page application. Then we might want to have the user authorization, where we need to use Authorization level anonymous with custom authorization functionality.

Here we learn how to write customized authorization for HTTP Trigger Function using Custom Binding (a way of declaratively connecting another functionality to the function).

JWTAuthorizationExtensionProvider

This class is responsible for adding a binding rule to the azure function configuration. While adding the binding rule, a link is established between a custom attribute(JWTAuthorizationAttribute) with the binding rule (JWTAuthorizationBindingProvider).

Parameter ExtensionConfigContext in Initialize method of IExtensionConfigProvider interface helps us to define on which attribute we need to add binding which returns an object of FluentBinding class of type JWTAuthorizationAttribute.

Now use the Bind method of FluentBinding class to actually define the rule i.e JWTAuthorizationBindingProvider in our example.

using AzureFunctionAuthorization.Attributes;
using AzureFunctionAuthorization.IService;
using Microsoft.Azure.WebJobs.Description;
using Microsoft.Azure.WebJobs.Host.Config;
namespace AzureFunctionAuthorization.Binding {
[Extension(name: "JWTAuthorizationBinding")]
public class JWTAuthorizationExtensionProvider : IExtensionConfigProvider{
private readonly IJWTHandlerService _jWTHandlerService;
public JWTAuthorizationExtensionProvider(IJWTHandlerService jWTHandlerService) {
_jWTHandlerService = jWTHandlerService;
}
public void Initialize(ExtensionConfigContext context) {
var binding = context.AddBindingRule<JWTAuthorizationAttribute>()
.Bind(new JWTAuthorizationBindingProvider(_jWTHandlerService));
}
}
}

JWTHandlerService

This is a custom class that implements the IJWTHandlerService which help us to create, validate and parse token. For the complete code of this file please go to GitHub repo.

JWTAuthorizationAttribute

Simple attribute with no special code, but it’s used by JWTAuthorizationExtensionProvider to link a parameter in the Azure Function with a binding rule as mentioned above.

using Microsoft.Azure.WebJobs.Description;
using System;
namespace AzureFunctionAuthorization.Attributes {
[Binding]
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
public class JWTAuthorizationAttribute : Attribute {
}
}

JWTAuthorizatinBindingProvider

This class implements the interface IBindingProvider and works as a factory for our custom binding rule. The JWTAuthorizationExtensionProvider inserts an instance of this class in the Azure Function configuration.

TryCreateAsync method of IBindingProvider interface will return IBinding, so we have to create a class that implements the IBinding interface I.e. JWTAuthorizationBinding in our example.

using AzureFunctionAuthorization.IService;
using Microsoft.Azure.WebJobs.Host.Bindings;
using System.Threading.Tasks;
namespace AzureFunctionAuthorization.Binding {
public class JWTAuthorizationBindingProvider : IBindingProvider {
private readonly IJWTHandlerService _jWTHandlerService;
public JWTAuthorizationBindingProvider(IJWTHandlerService jWTHandlerService) {
_jWTHandlerService = jWTHandlerService;
}
public Task<IBinding> TryCreateAsync(BindingProviderContext context) {
return Task.FromResult<IBinding>(new JWTAuthorizationBinding(_jWTHandlerService));
}
}
}

JWTAuthorizationBining

This class implements the IBinding interface. This is the class injected inside the Azure Function configuration. It provides an instance of IValueProvider, responsible to extract the value from the HTTPRequest.

Parameter BindingContext in BindAsync method of the IBinding interface has the property BindingData which helps us to get HTTP Request object.

namespace AzureFunctionAuthorization.Binding {
public class JWTAuthorizationBinding : IBinding {
private readonly IJWTHandlerService _jWTHandlerService;
public JWTAuthorizationBinding(IJWTHandlerService jWTHandlerService) {
_jWTHandlerService = jWTHandlerService;
}
public bool FromAttribute => true;
public Task<IValueProvider> BindAsync(BindingContext context) {
var data = context.BindingData["req"] as HttpRequest;
return Task.FromResult<IValueProvider>(new JWTTokenProvider(data,_jWTHandlerService));
}
public Task<IValueProvider> BindAsync(object value, ValueBindingContext context) {
return null;
}
public ParameterDescriptor ToParameterDescriptor() {
return null;
}
}

JWTTokenProvider

This class implements the IValueProvider interface and this is responsible for extracting the token from the header using the HttpRequest instance provided by JWTAuthoriationBindng.

This is where we will validate the token value and return the JWTClaim object,

public class JWTTokenProvider:IValueProvider {
private HttpRequest _httprequest;
private readonly IJWTHandlerService _jWTHandlerService;
public JWTTokenProvider(HttpRequest httpRequest, IJWTHandlerService jWTHandlerService) {
_httprequest = httpRequest;
_jWTHandlerService = jWTHandlerService;
}
public async Task<object> GetValueAsync() {
var request = await _httprequest.ReadAsStringAsync() as object;
string token = _httprequest.Headers["Authorization"];
JWTClaim claim = null;
if (string.IsNullOrWhiteSpace(token)) { return claim; }
try
{
claim = _jWTHandlerService.ParseClaims(token);
}
catch (Exception e)
{
return claim;
}
return claim;
}
public string ToInvokeString() => string.Empty;
public Type Type => typeof(object);
}

Linking Everything

Last we need to register JWTAuthorizationExtensonProvider in Startup, which adds a binding rule to the attribute.

using AzureFunctionAuthorization.Binding;
using AzureFunctionAuthorization.Domain.ViewModel;
using AzureFunctionAuthorization.IService;
using AzureFunctionAuthorization.Service;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.DependencyInjection;
using System;
[assembly: FunctionsStartup(typeof(AzureFunctionAuthorization.Startup))]
namespace AzureFunctionAuthorization {
public class Startup : FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
var configuration = builder.GetContext().Configuration;
var services = builder.Services;
// Register JWTHandlerSwervice, used to create parse and validate token
services.AddSingleton<IJWTHandlerService, JWTHandlerService>(x => {
return new JWTHandlerService(
new JWTConfiguration {
AccessTokenSecret = configuration["Token:AccessTokenSecret"],
AccessTokenExpiryTimeMiliseconds = Convert.ToInt64(configuration["Token:AccessTokenExpiryTime"]),
RefreshTokenExpiryTimeMiliseconds = Convert.ToInt64(configuration["Token:RefreshTokenExpiryTime"]),
Audience = configuration["Token:Audience"],
Issuer = configuration["Token:Issuer"],
}
);
});
// Register JWTAuthorizationExtesionProvider
var webJobs = services.AddWebJobs(x => { return; });
webJobs.AddExtension<JWTAuthorizationExtensionProvider>();
}
}
}
view raw Startup.cs hosted with ❤ by GitHub

Using the Binding in a Function

Finally, we are done with our first binding, now time to use this.

/// <summary>
/// Use Attribute which will help to check token
/// </summary>
/// <param name="req"></param>
/// <param name="log"></param>
/// <returns></returns>
[FunctionName(nameof(HttpTriggerFunction.TestAuthorization))]
public IActionResult TestAuthorization(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger log,
[JWTAuthorization()] JWTClaim jwtClaim) {
log.LogInformation("C# HTTP trigger function processed a request.");
// Check token is valid or not
if (jwtClaim == null)
{
return new UnauthorizedObjectResult("Invalid token");
}
return new OkObjectResult("Valid Token");
}

Final Comment

Today, we learned how to implement custom Authorization in HTTP Azure Function using custom binding.

I hope, after reading this you would be able to add Authorization on AzureFunctions. For complete code please visit GitHub repo.

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 )

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