Reputation: 1316
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
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....
}
Upvotes: 0
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
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