Jwt Bearer Authentication in a function app

Parag Kale 25 Reputation points
2025-04-29T23:54:36.0666667+00:00

Hi,

Is there an example available of implementing jwt bearer authentication in a function app ?

I am using the durable function monitor tool https://github.com/microsoft/DurableFunctionsMonitor in injected mode in isolated worker model.

I want to authenticate calls to /api/durable-functions-monitor end point , because my other functions in the function app use authorization level as Function and we don't want to establish a separate dedicated function app just for durable function monitor

Hence what we are trying to accomplish is use jwt bearer authentication just to secure the endpoint /api/durable-functions-monitor

Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
5,911 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Dasari Kamali 425 Reputation points Microsoft External Staff Moderator
    2025-05-07T06:07:07.5666667+00:00

    Hi @Parag Kale,

    Follow the below steps to authenticate a Durable function using the JWT Bearer Token.

    Create an Azure AD app and add the Microsoft Azure PowerShell (1950a258-227b-4e31-a9cf-717495945fc2) in Authorized client applications as shown below.

    1

    Run the below commands in Powershell to retrieve the Access Token.

    $tenantId = 'tenantId'  
    Connect-AzAccount -Tenant $tenantId -UseDeviceAuthentication 
    (Get-AzAccessToken -ResourceUrl "api://<60efd83bxxx>").Token
    

    2

    Below is the sample Durable function code to authenticate using the JWT Bearer token.

    using System.IdentityModel.Tokens.Jwt;
    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Azure.Functions.Worker.Http;
    using Microsoft.DurableTask;
    using Microsoft.DurableTask.Client;
    using Microsoft.Extensions.Logging;
    using Microsoft.IdentityModel.Protocols;
    using Microsoft.IdentityModel.Protocols.OpenIdConnect;
    using Microsoft.IdentityModel.Tokens;
    
    namespace FunctionApp62
    {
        public static class Function1
        {
            private const string Issuer = "https://login.microsoftonline.com/<tenantID>/v2.0";
            private const string Audience = "<ClientID>";
            private static readonly string MetadataAddress = $"{Issuer}/.well-known/openid-configuration";
    
            private static readonly ConfigurationManager<OpenIdConnectConfiguration> ConfigManager =
                new ConfigurationManager<OpenIdConnectConfiguration>(
                    MetadataAddress,
                    new OpenIdConnectConfigurationRetriever());
    
            [Function(nameof(Function1))]
            public static async Task<List<string>> RunOrchestrator(
                [OrchestrationTrigger] TaskOrchestrationContext context)
            {
                var logger = context.CreateReplaySafeLogger(nameof(Function1));
                logger.LogInformation("Running orchestrator...");
    
                var outputs = new List<string>
                {
                    await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"),
                    await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"),
                    await context.CallActivityAsync<string>(nameof(SayHello), "London")
                };
                return outputs;
            }
    
            [Function(nameof(SayHello))]
            public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
            {
                var logger = executionContext.GetLogger("SayHello");
                logger.LogInformation("Saying hello to {name}.", name);
                return $"Hello {name}!";
            }
    
            [Function("DurableFunctionsMonitor")]
            public static async Task<HttpResponseData> HttpStart(
                [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] Microsoft.Azure.Functions.Worker.Http.HttpRequestData req,
                [DurableClient] DurableTaskClient client,
                FunctionContext executionContext)
            {
                var logger = executionContext.GetLogger("DurableFunctionsMonitor");
                if (!req.Headers.TryGetValues("Authorization", out var authHeaders))
                {
                    var unauthorizedResponse = req.CreateResponse(System.Net.HttpStatusCode.Unauthorized);
                    await unauthorizedResponse.WriteStringAsync("Missing Authorization header.");
                    return unauthorizedResponse;
                }
                var authHeader = authHeaders.FirstOrDefault();
                if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
                {
                    var unauthorizedResponse = req.CreateResponse(System.Net.HttpStatusCode.Unauthorized);
                    await unauthorizedResponse.WriteStringAsync("Missing or invalid Authorization header.");
                    return unauthorizedResponse;
                }
                var token = authHeader.Substring("Bearer ".Length).Trim();
                if (!await ValidateJwtTokenAsync(token, logger))
                {
                    var unauthorizedResponse = req.CreateResponse(System.Net.HttpStatusCode.Unauthorized);
                    await unauthorizedResponse.WriteStringAsync("Invalid token.");
                    return unauthorizedResponse;
                }
                string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(Function1));
                logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);
                return await client.CreateCheckStatusResponseAsync(req, instanceId);
            }
            private static async Task<bool> ValidateJwtTokenAsync(string token, ILogger logger)
            {
                var tokenHandler = new JwtSecurityTokenHandler();
    
                try
                {
                    var config = await ConfigManager.GetConfigurationAsync(CancellationToken.None);
                    var validationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidIssuer = Issuer,
                        ValidateAudience = true,
                        ValidAudience = Audience,
                        ValidateLifetime = true,
                        RequireExpirationTime = true,
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKeys = config.SigningKeys
                    };
    
                    tokenHandler.ValidateToken(token, validationParameters, out _);
                    logger.LogInformation("Token validated successfully.");
                    return true;
                }
                catch (Exception ex)
                {
                    logger.LogError($"Token validation failed: {ex.Message}");
                    return false;
                }
            }
        }
    }
    

    Local Output :

    We can’t open http://localhost:7220/api/DurableFunctionsMonitor directly in the browser because it requires an Access Token. So, I first tested it using the below curl command in the command prompt to get the output.

    curl http://localhost:7220/api/DurableFunctionsMonitor -H "Authorization: Bearer <AccessToken>"
    

    8

    Then, I accessed [[http://localhost:7220/runtime/webhooks/durabletask/instances/f4b174d2xxxx?code=0CAtdJuMyk8zxxxxx]] in the browser.

    9

    Terminal Output :

    10

    Azure Function App Output :4 Hope this helps.

    If the answer is helpful, please click Accept Answer and kindly upvote it. If you have any further questions about this answer, please click Comment.

    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.