Introduction

As part of the Azure Static Web App functionality it provides authentication-related user information which can be accessed directly or through associated Azure Functions. As many user interfaces require information about a logged in user it is important to be able to access it. The information which can be accessed is referred to by Microsoft as “client principal data” (more information)[https://docs.microsoft.com/en-gb/azure/static-web-apps/user-information?tabs=csharp#client-principal-data].

Show me the Code!

TL;DR the code for this post can be found - https://github.com/WestDiscGolf/ClaimsPrincipalAccessor

Accessing from C#

As part of requests to Azure functions the “client principal data” is accessible through the x-ms-client-principal Http Header. Through the code example found - https://docs.microsoft.com/en-gb/azure/static-web-apps/user-information?tabs=csharp#api-functions - this is relatively easy to convert from the encoded header value through to a ClaimsPrincipal instance which can then be interrogated.

Accessing it in “out of process” model

The example code in the link above is based on the v3 / in process Azure Functions which gives you access directly to the HttpRequest instance. As with a number of concepts the data is still there but due to the nature of the out of process or isolated model it is not in a HttpRequest anymore but HttpRequestData.

This request payload still has access to the headers, body, cookies, method, uri etc. The headers collection accessible is still derived from System.Net.Http.Headers.HttpHeaders in the base configuration so should give familiarity to your code.

An updated version of the “client principal data” processing method is relatively straightforward to update and to use the newer construct. I have also converted it to be an extension method as well to aid with usage.

    public static class ClaimsPrincipalProcessing
    {
        private class ClientPrincipal
        {
            public string IdentityProvider { get; set; }

            public string UserId { get; set; }

            public string UserDetails { get; set; }

            public IEnumerable<string>? UserRoles { get; set; }
        }

        /// <summary>
        /// Code below originally from Microsoft Docs - https://docs.microsoft.com/en-gb/azure/static-web-apps/user-information?tabs=csharp#api-functions
        /// </summary>
        /// <param name="req">The HttpRequestData header.</param>
        /// <returns>Parsed ClaimsPrincipal from 'x-ms-client-principal' header.</returns>
        public static ClaimsPrincipal ParsePrincipal(this HttpRequestData req)
        {
            var principal = new ClientPrincipal();

            if (req.Headers.TryGetValues("x-ms-client-principal", out var header))
            {
                var data = header.First();
                var decoded = Convert.FromBase64String(data);
                var json = Encoding.UTF8.GetString(decoded);
                principal = JsonSerializer.Deserialize<ClientPrincipal>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
            }

            principal.UserRoles = principal.UserRoles?.Except(new[] { "anonymous" }, StringComparer.CurrentCultureIgnoreCase);

            if (!principal.UserRoles?.Any() ?? true)
            {
                return new ClaimsPrincipal();
            }

            var identity = new ClaimsIdentity(principal.IdentityProvider);
            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, principal.UserId));
            identity.AddClaim(new Claim(ClaimTypes.Name, principal.UserDetails));
            identity.AddClaims(principal.UserRoles.Select(r => new Claim(ClaimTypes.Role, r)));

            return new ClaimsPrincipal(identity);
        }
    }

This works really well however it still feels a little clunky to me. Why does it feel clunky? Well because the Azure function has to call the extension method itself and you may want access to the ClaimsPrincipal instance in a dependency injected service and not directly in the Azure Function. If you’re not in the Azure Function then you don’t have access to the HttpRequestData instance. So how do we solve this?

Middleware to the rescue

As with ASP.NET Core the concept of middleware is a way to intercept the current request and look at the data on the way in and again on the way out. These requests apply to all the specific types, in this case Http Requests, and the payload and headers etc. can be interrogated.

The difference, at least at the moment, with Azure Functions middleware is the functionality is not as rich as ASP.NET Core and due to this a number of the items you would expect are either not currently there or hidden internally. To access the data we require for this there is some stringly typing and reflection going on. While this is not ideal I do believe that it will be made easier to use in future versions. But for now we are where we are!

To access the “x-ms-client-principal” header value we first need to get access to the HttpRequestData. As Azure Functions can be of a number of different types this isn’t just presented to you but we have to go and find it from the FunctionContext passed in.

The signature of the IFunctionsWorkerMiddleware has similarities to ASP.NET Core middleware.

public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)

It has an Invoke method which has a context and the next delegate to call in the pipeline. The context is the one we are interested in at this point. The FunctionContext encapsulates the information, both metadata and user supplied data, about the current function execution. It has access to the InvocationId, trace context, binding context information etc. but the target one we’re interested in is the IInvocationFeatures collection. It has some handy helper methods on it for getting and setting features which are strongly typed but as the one we’re interested in is internal we have to use the name, “IFunctionBindingsFeature”.

(Type featureType, object featureInstance) = context.Features.SingleOrDefault(x => x.Key.Name == "IFunctionBindingsFeature");

We now have access to an instance of Microsoft.Azure.Functions.Worker.Context.Features.GrpcFunctionBindingsFeature which is the default for this type (as far as I can make out!).

As the IInvocationFeatures collection implements IEnumerable<KeyValuePair<Type, object>> we can destruct the key value pair into the individual items to make accessing them clearer.

We now want access to the InputData of the function call. We can use reflection to find the appropriate property name on the “featureType” and then use it to get the data from the “featureInstance”.

var inputData = featureType.GetProperties().SingleOrDefault(p => p.Name == "InputData")?.GetValue(featureInstance) as IReadOnlyDictionary<string, object>;

This will return an IReadOnlyDictionary<string, object> of the function being called input values. The input values will contain the HttpRequestData instance which we want to access as this has the Header collection on it.

var requestData = inputData?.Values.SingleOrDefault(obj => obj is HttpRequestData) as HttpRequestData;

Once we have the HttpRequestData we can now use the extension method as defined above to parse the header value into the ClaimsPrincipal instance we want.

But now what? How do we put it somewhere which is useful?

ClaimsPrincipalAccessor

With ASP.NET Core there has been a concept for a while now of an IHttpContextAccessor which is registered with the DI system and allows for accessing the HttpContext of the current request without injecting it directly. This allows for DI techniques to let Http based values be accessed by parts of the system which have no idea about Http. In the same way this is how I have allowed for accessing the ClaimsPrincipal without the need for the consuming code to understand where it has come from.

Taking inspiration from the ASP.NET Core HttpContextAccessor implementation - https://github.com/dotnet/aspnetcore/blob/8b30d862de6c9146f466061d51aa3f1414ee2337/src/Http/Http/src/HttpContextAccessor.cs - I have created an accessor which deals with the ClaimsPrincipal instead.

    public class ClaimsPrincipalAccessor : IClaimsPrincipalAccessor
    {
        private readonly AsyncLocal<ContextHolder> _context = new();

        public ClaimsPrincipal? Principal
        {
            get => _context.Value?.Context;
            set
            {
                var holder = _context.Value;
                if (holder is not null)
                {
                    holder.Context = null;
                }

                if (value is not null)
                {
                    _context.Value = new ContextHolder { Context = value };
                }
            }
        }

        private class ContextHolder
        {
            public ClaimsPrincipal? Context;
        }
    }

To access this we need to first register it with the DI system in the HostBuilder.

var host = new HostBuilder()
                .ConfigureServices(services =>
                {
                    services.AddSingleton<IClaimsPrincipalAccessor, ClaimsPrincipalAccessor>();
                })
                .ConfigureFunctionsWorkerDefaults(worker =>
                {
                    worker.UseMiddleware<ClaimsPrincipalMiddleware>();
                })
                .Build();

            host.Run();

Now that it is registered we can access it through the FunctionContext in the middleware using the InstanceServices collection.

if (requestData?.ParsePrincipal() is ClaimsPrincipal principal)
{
    // set the principal on the accessor from DI
    var accessor = context.InstanceServices.GetRequiredService<IClaimsPrincipalAccessor>();
    accessor.Principal = principal;
}

Now the value has been set we can continue the processing flow of the Azure Function call and then consume the value.

Full Middleware Implementation

Below is the full middleware implementation.

    public class ClaimsPrincipalMiddleware : IFunctionsWorkerMiddleware
    {
        public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
        {
            // determine the type, the default is Microsoft.Azure.Functions.Worker.Context.Features.GrpcFunctionBindingsFeature
            (Type featureType, object featureInstance) = context.Features.SingleOrDefault(x => x.Key.Name == "IFunctionBindingsFeature");

            // find the input binding of the function which has been invoked and then find the associated parameter of the function for the data we want
            var inputData = featureType.GetProperties().SingleOrDefault(p => p.Name == "InputData")?.GetValue(featureInstance) as IReadOnlyDictionary<string, object>;
            var requestData = inputData?.Values.SingleOrDefault(obj => obj is HttpRequestData) as HttpRequestData;

            if (requestData?.ParsePrincipal() is ClaimsPrincipal principal)
            {
                // set the principal on the accessor from DI
                var accessor = context.InstanceServices.GetRequiredService<IClaimsPrincipalAccessor>();
                accessor.Principal = principal;
            }

            await next(context);
        }
    }

Consuming the ClaimsPrincipal value in an Azure Function

Much like consuming the IHttpContextAccessor the IClaimsPrincipalAccessor can now be injected into any of your services through constructor injection. This also includes your Azure Function. The value can then be accessed as any other dependency injected service.

    public class GetWelcome
    {
        private readonly IClaimsPrincipalAccessor _claimsPrincipalAccessor;

        public GetWelcome(IClaimsPrincipalAccessor claimsPrincipalAccessor)
        {
            _claimsPrincipalAccessor = claimsPrincipalAccessor;
        }

        [Function("GetWelcome")]
        public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequestData req,
            FunctionContext executionContext)
        {
            var logger = executionContext.GetLogger("GetWelcome");
            logger.LogInformation("C# HTTP trigger function processed a request.");

            var msg = new Message { Value = "welcome to Azure Functions!" };
            if (_claimsPrincipalAccessor.Principal?.Identity?.IsAuthenticated == true)
            {
                msg.Value = $"Welcome to Azure Functions {_claimsPrincipalAccessor.Principal.Identity.Name}!!";
            }

            var response = req.CreateResponse(HttpStatusCode.OK);
            await response.WriteAsJsonAsync(msg);
            return response;
        }
    }

    public class Message
    {
        public string Value { get; set; }
    }

From the above we can see that the accessor is injected into the GetWelcome class. This can then be accessed in the Function method like any other injected dependency.

if (_claimsPrincipalAccessor.Principal?.Identity?.IsAuthenticated == true)
{
    msg.Value = $"Welcome to Azure Functions {_claimsPrincipalAccessor.Principal.Identity.Name}!!";
}

Conclusion

In this post we have reviewed how to parse the ClaimsPrincipal from the Azure Static Web Apps header which can be imitated by the local CLI dev experience. We have looked at how to use Azure Functions middleware in isolated mode to interrogate the headers being passed in and parse the ClaimsPrincipal header to allow for easier access throughout the rest of the codebase.

What have I learnt from this? Well the middleware experience does seem to be a little bit limited at the moment. This isn’t unexpected as the .NET 5/6 out of process model is still very early in it’s life time and I would imagine the product team don’t want to expose too much too soon while they work out the plan for moving forward. I can see items moving, constructs being made available and accessibility to these constructs getting easier over time as more middleware functionality starts to be used in the functions space.

What are your thoughts on Azure Functions middleware? What would you like to see become available? Let me know on Twitter!