What is the best practice/approach to authenticate Entra ID access tokens from multiple issuers in a dotnet6 Function App?

Neil Barber 20 Reputation points
2023-09-28T16:17:24.77+00:00

Hi,

Is there a best practice or approach to authenticate an access token within a dotnet6 function app?

Ideally, I'd like to call a standard library to perform the authentication, however I've been unable to find one that works. I've previously called the Microsoft.IdentityWeb library under dotnet3.1, however this doesn't appear to be supported in Function Apps running dotnet6, which I believe is due to this still open issue https://github.com/AzureAD/microsoft-identity-web/issues/1548

The only way I've found to authenticate an access token within a function app, is by manually calling the JwtSecurityTokenHandler, and OpenIdConnectConfiguration to obtain the SigningKeys using the Microsoft.IdentityModel.Protocols.OpenIdConnect and System.IdentityModel.Tokens.Jwt libraries (as per example code below).

Although this allows me to easily include and validate the token against multiple issuers, is there a standard library I can call, or a better approach I should consider?

  // Validate token
            var openIdConfig = await _memoryCache.GetOrCreateAsync(CacheOpenIdConfigKey, async (ce) =>
            {
                ce.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
                var openIdConfigManager = new ConfigurationManager<OpenIdConnectConfiguration>("https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
                return await openIdConfigManager.GetConfigurationAsync();
            });
            var tokenHandler = new JwtSecurityTokenHandler();
            JwtSecurityToken token;

            try
            {
                tokenHandler.ValidateToken(queryArgs.EntraToken,
                    new TokenValidationParameters
                    {
                        ValidAudience = ValidAudience, 
                        IssuerSigningKeys = openIdConfig.SigningKeys,
                        ValidIssuers = await GetValidIssuers(),
                        ClockSkew = TimeSpan.Zero
                    }, out SecurityToken validatedToken);

                token = validatedToken as JwtSecurityToken;
            }
            catch (SecurityTokenValidationException ex)
            {
                return Result.Fail<TR>(new ProblemDetails { Status = HttpStatusCode.Unauthorized, Title = "Token is Unauthorized", Detail = "Token is not registered for the tenant service" });
            }
Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,001 questions
0 comments No comments
{count} votes

Accepted answer
  1. MuthuKumaranMurugaachari-MSFT 22,331 Reputation points
    2023-09-29T14:42:17.5833333+00:00

    Neil Barber Thanks for posting your question in Microsoft Q&A. Have you considered using built-in authentication and authorization capabilities known as Easy Auth? The middleware handles the authentication of all incoming HTTP requests and extract the identity data from HTTP headers to ClaimsPrincipal object. You can simply validate the claims in your application.

    However, if you cannot use it for your scenarios, then checkout AzureFunctions.Authentication library which provides the following solution:

    1. Expose custom Authentication/Authorization builder extensions that dont override existing one but registers all needed services
    2. Provide custom extension that derives from IExtensionConfigProvider
    3. Re-configure already configured Authentication/Authorization by Azure Functions
    4. Dynamically inject new authentication schema and handler since Bearer schema is used by Azure Functions with their handler
    5. Override IAuthorizationHandlerProvider to merge Azure Functions handlers with application handlers

    The problem with current Function implementation is detailed here (Override host services) and the repo offers examples, code snippet for you to follow through.

    I hope this helps and let me know if you have any questions.


    If you found the answer to your question helpful, please take a moment to mark it as "Yes" for others to benefit from your experience. Or simply add a comment tagging me and would be happy to answer your questions.

    1 person found this answer helpful.
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Thomas Meads 1,586 Reputation points
    2023-09-28T17:01:11.97+00:00

    Hi,

    Yes the library you are looking for the Azure.Identity: https://learn.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme?view=azure-dotnet.

    Using the library you have some options depending on your setup using the class DefaultAzureCredential which you can read here: https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet. If it is in an environment where there is a managed identity assigned such as a function or web app then simply creating the object will get the context you need.

    Once you have created the DefaultAzureCredential object you can get an access token by:

    var defaultCredential = new DefaultAzureCredential();
    
    var accessToken = await defaultCredential.GetTokenAsync(new []{"user.read"});
    
    
    

    It should also be noted that most if not all of the specific service SDKs will use the DefaultAzureCredential class directly E.g. the Azure Key Vault SDK.

    Hopefully this is what you were looking for.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.