Tarostar
Tarostar

Reputation: 1316

Get function key (name) used when calling Azure Function

I need to be able to identify the key (ideally key name) provided in the header (x-functions-key) for the POST to the Azure Function in the Run method, e.g.

Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log, ClaimsPrincipal principal)

It is great to be able to protect access to the Azure Function adding Function Keys in the Azure Portal panel, but I must be able to tell which function key was used. Ideally it would be possible to associate claims on each function key, but as long as I can at least figure out which key was used I will be happy.

Upvotes: 6

Views: 3991

Answers (3)

CajunCoding
CajunCoding

Reputation: 969

For those interested here's a custom extension that uses the Claim as noted in prior answers to get the App Key Name easily from the User ClaimsPrincipal in Azure Functions.

In-Process Azure Function:

I believe this works with all versions but has been tested with Az Func v4.

/// <summary>
/// Read the current Azure Function App Key Name (the name of the token as configured in azure portal).
/// </summary>
public static string GetCurrentAzFuncAppTokenKeyName(this ClaimsPrincipal userPrincipal)
{
    const string AzFuncKeyClaimUri = "http://schemas.microsoft.com/2017/07/functions/claims/keyid";
    return userPrincipal?.Claims.FirstOrDefault(c => c.Type == AzFuncKeyClaimUri)?.Value;
}

In-process Usage (using ClaimsPrincipal) would be:

[FunctionName(nameof(DoTheThing))]
public Task<IActionResult> DoTheThing(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "do-the-thing")] HttpRequest httpRequest
)
{
    var azFuncKeyName = httpRequest.HttpContext.User.GetCurrentAzFuncAppTokenKeyName()
                        ?? throw new UnauthorizedAccessException("Azure Function Key Name claim was not found.");

    //Do all the things....
}

Isolated Process Azure Function:

In the isolated process model you can't get the ClaimsPrincipal in the same way; it doesn't exist on the HttpRequestData model and if you try and use DI it'll simply be null. But the HttpRequestData instead has an Identities collection...

This was tested with Az Funcs v4 using .NET the latest Isolated model libraries Microsoft.Azure.Functions.Worker v1.21.0...

NOTE: This apparently does NOT work if you are using the (seemingly new compatibility library) Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore with the configuration method new HostBuilder().ConfigureFunctionsWebApplication() (which is non-standard).

So you need to be sure you are using the vanilla isolated function model with only the host configuration method new HostBuilder().ConfigureFunctionsWorkerDefaults()!

/// <summary>
/// Read the current Azure Function App Key Name (the name of the token as configured in azure portal).
/// </summary>
public static string? GetCurrentAzFuncAppTokenKeyName(this HttpRequestData httpRequestData)
{
    const string AzFuncKeyClaimUri = "http://schemas.microsoft.com/2017/07/functions/claims/keyid";
    return httpRequestData?.Identities.SelectMany(i => i.Claims).FirstOrDefault(c => c.Type == AzFuncKeyClaimUri)?.Value;
}

Isolated process Usage (using HttpRequestData) would be:

[FunctionName(nameof(DoTheIsolatedThing))]
public Task<IActionResult> DoTheIsolatedThing(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "do-the-isolated-thing")] HttpRequestData httpRequestData
)
{
    var azFuncKeyName = httpRequestData.GetCurrentAzFuncAppTokenKeyName()
        ?? throw new UnauthorizedAccessException("Azure Function Key Name claim was not found.");

    //Do all the isolated things....
}
  • Edit: Enabled code formatting for csharp.
  • Edit: Fixed Example code. Added example for Azure Functions Isolated Model.

Upvotes: 0

Fowl
Fowl

Reputation: 5128

In Azure Functions v1:

[FunctionName("MyAuthenticatedFunction")]
public static async Task<HttpResponseMessage> MyAuthenticatedFunction([HttpTrigger(AuthorizationLevel.Function)] System.Net.Http.HttpRequestMessage reqMsg, ILogger log)
{
    if (reqMsg.Properties.TryGetValue("MS_AzureFunctionsKeyId", out object val))
        log.LogInformation($"MS_AzureFunctionsKeyId: {val}");  
}

Code reference: WebJobs.Script.WebHost/Filters/AuthorizationLevelAttribute.cs#L77

Upvotes: 0

Stefan
Stefan

Reputation: 141

Simply get the claim "http://schemas.microsoft.com/2017/07/functions/claims/keyid" from the req.HttpContext.User.Claims object. It contains the key id in case a Function key was used.

Works like a charm, and does not require external lookups.

const string KEY_CLAIM = "http://schemas.microsoft.com/2017/07/functions/claims/keyid";
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
    var claim = req.HttpContext.User.Claims.FirstOrDefault(c => c.Type == KEY_CLAIM);
    if (claim == null) 
    {
        log.LogError("Something went SUPER wrong");
        throw new UnauthorizedAccessException();
    }
    else
    {
        log.LogInformation( "Processing call from {callSource}", claim.Value);
    }

Upvotes: 8

Related Questions