Authentication & Authorization in Azure Functions with Azure Active Directory using C#/.NET
Implementing a simple reusable authorization attribute using the Microsoft Identity Platform to validate access tokens and authorize users based on their app roles in serverless Azure Function apps.
Azure offers a builtin middleware for Authentication the can be easily configured in the Azure Portal and allows for simple authorization tasks. But it also has two major disadvantages:
- works only when the functions runs in Azure. So no local developer testing and no running the function app outside of Azure e.g. in a container.
- doesn’t support more advanced authorization needs, e.g. validation user membership in a app role
- near impossible to debug if something is going wrong inside the middleware. I just had a case where the middleware returns a 500 http response when used with tokens from a Outlook addin and I’ve yet to figure out why.
Luckily we can implement our own “middleware” using the FunctionInvocationFilterAttribute in the Azure.WebJobs.Host namespace and use JwtSecurityTokenHandler to validate access tokens.
Note: This class is marked as obsolete as it’s a feature that has been in preview since 2018. Join the discussion on this here: https://github.com/Azure/azure-webjobs-sdk/issues/1284
This solution works anywhere where the Azure Functions runtime can execute, also for local dev testing and can be used with any OpenID Connect/OAuth 2.0 compatible identity system like Azure Active Directory.
Ultimately we want the same developer productivity and convenience that we are used to from ASP.NET’s identity system in our functions so that we could do some like this:
[Authorize]
[FunctionName(“TestFunction”)]
public static async Task<IActionResult> Run(…
The idea is to decorate any function with the Authorize attribute to enable authorization for the given function. This also allows to apply authorization for selected functions and can be extended to also incorporate app roles:
[Authorize(“admin”)]
[FunctionName(“TestFunction”)]
public static async Task<IActionResult> Run(
Please find the full sample implementation here: https://github.com/GrillPhil/NetCoreFunctionsAuthSample
Prerequisites
- Visual Studio or VS Code with the Azure Functions Extensions
- Azure Active Directory Tenant with a App Registration configured
So let’s get started:
Step 1: Setup Function App
Create a new Function app in Visual Studio or VS Code and add Nuget packages:
- Microsoft.IdentityModel.Protocols.OpenIdConnect (5.5.0)
- System.IdentityModel.Tokens.JWT (5.5.0)
Step 2: Validate access token
To handle token validation create a new static class AuthHandler and add some helper methods to facilitate extraction of the access token from a http request:
Also let’s add some helpers to handle authority and valid issuers:
To perform the actual token validation we need to fetch the OpenIdConnectConfiguration from the authority, configure validation parameters like the valid audience and issuer and finally validate the access token. TenantId, clientId and the audience (aka App Id Uri in the app registraion in AAD) are provided as environement variables.
Note the parameters for ValidIssuers. If you are building a multi tenant app use common as tenantId instead of your “home” tenant id to disable issuer validation and allow access tokens issued by multiple different tenants.
Last step for the AuthHandler is to wrap the methods for easy use:
Step 3: Custom Authorize Attribute
To make the authorization comfortable to apply to functions we can introduce a AuthorizeAttribute that inherits from FunctionInvocationFilterAttribute and override the OnExecutingAsync method that is called before the function’s method is called:
Note how we add the validated user’s identities to the existing identities of the actual request or return 401 if now user could be validated. To access the user object in a function’s method it can be added as parameter like so:
Bonus: Authorize using app roles
If you need to grant access based on the user’s role I recommend using AAD’s app roles. Our setup can be extended to also validate the user’s app role. Therefore we allow to pass in valid app roles to the AuthorizeAttribute:
Then we add a method to check if the user’s actual roles (provided as claim as part of the access token) overlap with the valid roles provided in the AuthorizeAttribute:
Finally call the method in the AuthorizeAttribute’s OnExecutingAsync method:
Summary
Implementing the token validation in code instead of using the builtin authorization middleware in Azure gives us some unique advantages:
- Run and debug anywhere, e.g. as container and in local development
- Apply only for some functions vs. applied on the entire function app
- Authorize based on app roles
- Use any OAuth 2.0/OpenID Connect compatible system
That’s it folks. Hope you find this helpful when you need to authenticate and authorize users in your function app.