5

Is it possible to have Filters (Auth or Exception) for Azure functions? I just want to not duplicate code to validate bearer token in every function. I see that there is a filter concept in webjobs sdk. https://github.com/Azure/azure-webjobs-sdk/wiki/Function-Filters

I want to only validate the bearer token before executing any function. So if filters are not the best option then is there any other better way to handle this situation ?

Venkata Dorisala
  • 4,783
  • 7
  • 49
  • 90
  • 1
    Not sure if you are asking whether you can use Function Filters for Functions (yes), or for alternatives? If the latter, refine "better way" please. – Mikhail Shilkov Oct 11 '17 at 10:07
  • @Mikhail . I mean't if filters are not the best option for handling duplicated code.. then is there any other way of handling this. I want only bearer token to be validated before executing any function. – Venkata Dorisala Oct 11 '17 at 10:19

2 Answers2

3

Depending on how feature rich you want your responses you could use function filtered but they are very limited at the moment until this issue has been completed - https://github.com/Azure/azure-webjobs-sdk/issues/1314

Alternatively, you could set up a pipeline in each of your functions so you could apply the same cross-cutting concern logic inside your function app. obviously this will be a lot more work but comes with a lot more flexibility.

Example - https://github.com/kevbite/AzureFunctions.GreenPipes

Kevin Smith
  • 13,746
  • 4
  • 52
  • 77
  • 1
    Thanks @kevin . do you mind elaborating on pipeline approach. any sample code if possible? – Venkata Dorisala Oct 11 '17 at 10:18
  • You could use something like `GreenPipes` https://drusellers.com/greenpipes/2016/10/30/greenpipes.html or just build a simple chain yourself to apply the same logic. Sadly I've not got any code examples using azure functions. – Kevin Smith Oct 11 '17 at 10:35
  • 1
    I now have a code example - https://github.com/kevbite/AzureFunctions.GreenPipes – Kevin Smith Oct 11 '17 at 12:32
0

Instead of bringing in another package, you can just pass your function code as an argument in to a wrapper method.

//business logic
[FunctionName("PostWidget")]
public async Task<IActionResult> PostWidget(
  [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "widgets")] Widget item, HttpRequest req, ILogger log)
{
  return await _functionWrapper.Execute(req, item, async () =>
  {
    log.LogInformation($"posting widget: ${item.Id}");

    var newItem = await dbContext.Widgets.AddAsync(item);
    await dbContext.SaveChangesAsync();

    return new ResponseEnvelopeResult<Widget>(HttpStatusCode.Created, newItem.Entity);
  });
}

//functionWrapper class
public async Task<IActionResult> Execute(T model, HttpRequest req, Func<Task<IActionResult>> azureFunction)
{
  var results = await _validator.ValidateAsync(model, ruleSet: $"default,audit,{req.Method}");
  if (!results.IsValid)
  {
    var errors = results.Errors.Select(x => x.ErrorMessage).ToList();
    _log.LogWarning($"Model validation failed for type '{typeof(T).Name}'. Validation errors: [{errors.Join()}] ");
    return new ResponseEnvelopeResult<T>(HttpStatusCode.BadRequest, null, errors);
  }

  try
  {
    return await azureFunction();
  }
  catch (Exception e)
  {
    _log.LogError(e, "Unhandled exception occured in FunctionWrapper");
    return new ResponseEnvelopeResult<T>(HttpStatusCode.InternalServerError, null, new[] { e.Message });
  }
}

Then your wrapper can be setup to do validation, retrieve user info, etc. If you need items passed back to your function layer, you can easily do so without obscuring your function intent. I've got a large example of this implementation on my blog.

https://blog.bruceleeharrison.com/2019/09/04/azure-v2-functions-with-fluentvalidation/

Lee Harrison
  • 2,306
  • 21
  • 32