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.
Run the below commands in Powershell to retrieve the Access Token.
$tenantId = 'tenantId'
Connect-AzAccount -Tenant $tenantId -UseDeviceAuthentication
(Get-AzAccessToken -ResourceUrl "api://<60efd83bxxx>").Token
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>"
Then, I accessed [[http://localhost:7220/runtime/webhooks/durabletask/instances/f4b174d2xxxx?code=0CAtdJuMyk8zxxxxx]] in the browser.
Terminal Output :
Azure Function App Output :
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.