I am using Azure Functions to handle large (>20MB) files encoded in base64 and passed inside a Json.
Before reading and parsing the JSON body, I need to authenticate the client via an API key passed in the header of the HTTP request.
Analyzing the logs, it seems that the entire JSON is read before even executing the function.
Is there a way I can delay the reading of the JSON body until after I authenticated the user?
Edit:
The function is declared like this:
public static class V1Functions
{
[FunctionName("V1MyFunction")]
public static async Task<IActionResult> MyFunction(
[HttpTriggerAttribute(AuthorizationLevel.Anonymous, "post", Route = "v1/my_function")] HttpRequest request,
ILogger logger)
{
//...
}
}
Create a timer triggered function In your function app, select Functions, and then select + Create. Select the Timer trigger template. Configure the new trigger with the settings as specified in the table below the image, and then select Create. Defines the name of your timer triggered function.
A timer trigger lets you run a function on a schedule. This is reference information for Azure Functions developers. If you're new to Azure Functions, start with the following resources: Azure Functions developer reference.
The HTTP trigger lets you invoke a function with an HTTP request. You can use an HTTP trigger to build serverless APIs and respond to webhooks. The default return value for an HTTP-triggered function is: HTTP 204 No Content with an empty body in Functions 2.
The HyperText Transfer Protocol (HTTP) 202 Accepted response status code indicates that the request has been accepted for processing, but the processing has not been completed; in fact, processing may not have started yet.
You can use Azure app service built-in authentication and authorization support(easy auth) to authenticate users. Details about Azure app service easy auth see here.
Azure functions are based on Azure app service, so it is enabled for Azure functions too.
This section indicates how it works:
Every incoming HTTP request passes through it before being handled by your application code.
I think this is the auth way that you are looking for: not handle request data in your code before users are authenticated. This blog will be helpful for you to use Azure function and Easy Auth.
Hope it helps.
I had a similar issue, where the incoming request had to be authenticated before executing. However, I did not really care about the size of the content (it should never reach e.g. ~20MB). Thus, I'm not sure if this approach actually fits your needs.
As you stated in your comment OnExecutingAsync
can be used for placing code to be executed before the function itself. The problem is, that OnExecutingAsync
is just a task; you can't really return anything from it - which would be nice if it was possible to do, as it would allow us to return e.g. "Unauhorized", which usually is the case when talking about http requests.
In any case, whether it adheres to your requirements or not, here goes: As I see it, the request holds the contents as a stream, and afaik it hasn't been processed by the http trigger pipeline yet, at least not 'till you call e.g. ReadAsStringAsync
or something like that.
Use a BaseController which implements IFunctionInvocationFilter
, something like:
Note: IFunctionInvocationFilter
is still in preview as of 2019-12-29.
internal abstract class BaseController : IFunctionInvocationFilter
{
protected bool _IsAuthenticated = false;
private IAuthenticationService _AuthenticationService;
protected BaseController(IAuthenticationService authenticationService)
{
_AuthenticationService = authenticationService;
}
public virtual async Task OnExecutedAsync(FunctionExecutedContext executedContext, CancellationToken cancellationToken)
{
_IsAuthenticated = false;
}
public virtual async Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken)
{
// This part is a tad flimsy, but I never managed to find a better way of retrieving the header values
// You could probably investigate the FunctionExecutingContext a tad more and see if you can come up with something better
if (executingContext.Arguments.TryGetValue("request", out var request) && request is HttpRequest httpRequest)
_IsAuthenticated = _AuthenticationService.Authenticate(httpRequest.Headers);
else
_IsAuthenticated = false;
}
protected async Task<IActionResult> DoAuthenticated(Func<Task<IActionResult>> action)
{
return !IsAuthenticated ? Unauthenticated() : await action();
}
protected virtual IActionResult Unauthenticated()
{
var someErrorHandlingWithSomeModelAsBody = ...
return new UnauthorizedObjectResult(someErrorHandlingWithSomeModelAsBody);
}
}
The AuthenticationService is the capable of determining whether or not the key in the header is valid or not.
You've probably noticed the use of dependency injection (it's just how the function app I made is implemented), you could probably easily rectify this making things static as needed.
A sample controller would be something like:
public class SampleController : BaseController
{
private readonly ISampleService _SampleService;
public SampleController(ISampleService sampleService, IAuthenticationService authenticationService) : base(authenticationService)
{
_SampleService = sampleService;
}
[FunctionName(nameof(SampleFunction))]
public async Task<IActionResult> SampleFunction(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "v1/my_function")] HttpRequest request,
ILogger log)
{
try
{
return await DoAuthenticated(async () =>
{
var largeFileAsString = await request.Content.ReadAsStringAsync();
return await _SampleService.HandleLargeFile(largeFileAsString);
}
}
catch (Exception e)
{
// If you need exception handling...
}
}
}
Whenever SampleFunction
is triggered, it will execute in the following order:
OnExecutingAsync
-bodySampleFunction
-body - the func will only be executed if the authentication is actually successful during OnExecutingAsync
OnExecutedAsync
-bodyThe question is if the function actually still processes the entire 20MB of JSON content you speak of, even when the authentication fails. I'd actually personally be interested in the results if you test this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With