Hey Mondal,
I had exactly the same problem with .NET 6 in process worker! But I solved it with implementing my own token validation service. Since AddAuthentication/AddAuthorization service collections are from ASP.NET CORE but not Azure Functions, i would suggest you to implement your own token validation service. I assume you also use .NET 6
{
"AzureAdB2C:Instance": "https://contosob2c.b2clogin.com/",
"AzureAdB2C:ClientId": "your_client_id",
"AzureAdB2C:ClientSecret": "your_client_secret",
"AzureAdB2C:Domain": "contosob2c.onmicrosoft.com",
"AzureAdB2C:SignUpSignInPolicyId": "B2C_1_SignUpSignIn",
"AzureAdB2C:ResetPasswordPolicyId": "B2C_1_ResetPassword",
"AzureAdB2C:EditProfilePolicyId": "B2C_1_EditProfile",
"AzureAdB2C:Issuer": "https://contosob2c.b2clogin.com/contosob2c.onmicrosoft.com/v2.0/",
"AzureAdB2C:MetadataUrl": "https://contosob2c.b2clogin.com/contosob2c.onmicrosoft.com/v2.0/.well-known/openid-configuration"
}
}
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Threading;
using System.Threading.Tasks;
namespace MyNamespace
{
public class JwtService
{
private readonly IConfiguration _configuration;
private readonly IConfigurationManager<OpenIdConnectConfiguration> _configurationManager;
public JwtService(IConfiguration configuration)
{
_configuration = configuration;
var metadataAddress = _configuration["AzureAdB2C:MetadataUrl"];
_configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataAddress, new OpenIdConnectConfigurationRetriever());
}
public async Task<(bool, HttpResponseData)> AuthenticateAndAuthorizeAsync(HttpRequestData req, FunctionContext executionContext, string[] acceptedScopes)
{
var response = req.CreateResponse();
if (!req.Headers.TryGetValues("Authorization", out var authHeaders) || !authHeaders.Any())
{
response.StatusCode = HttpStatusCode.Unauthorized;
await response.WriteStringAsync("Unauthorized");
return (false, response);
}
var authHeader = authHeaders.First();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
{
response.StatusCode = HttpStatusCode.Unauthorized;
await response.WriteStringAsync("Unauthorized");
return (false, response);
}
var bearerToken = authHeader.Substring("Bearer ".Length);
try
{
var openIdConfig = await _configurationManager.GetConfigurationAsync(CancellationToken.None);
var validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = _configuration["AzureAdB2C:Issuer"],
ValidateAudience = true,
ValidAudience = _configuration["AzureAdB2C:ClientId"],
ValidateLifetime = true,
IssuerSigningKeys = openIdConfig.SigningKeys
};
var tokenHandler = new JwtSecurityTokenHandler();
var principal = tokenHandler.ValidateToken(bearerToken, validationParameters, out SecurityToken validatedToken);
var claims = principal.Claims;
var scopes = claims.Where(c => c.Type == "scp").Select(c => c.Value).ToList();
if (!scopes.Any(scope => acceptedScopes.Contains(scope)))
{
response.StatusCode = HttpStatusCode.Forbidden;
await response.WriteStringAsync("Forbidden");
return (false, response);
}
}
catch (Exception ex)
{
var logger = executionContext.GetLogger("JwtService");
logger.LogError(ex, "Token validation failed");
response.StatusCode = HttpStatusCode.Unauthorized;
await response.WriteStringAsync("Unauthorized");
return (false, response);
}
return (true, null);
}
}
}
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using System.Net;
namespace MyNamespace
{
public class MyFunction
{
private readonly JwtService _jwtService;
private readonly ILogger<MyFunction> _logger;
public MyFunction(JwtService jwtService, ILogger<MyFunction> logger)
{
_jwtService = jwtService;
_logger = logger;
}
[Function("MyGetFunction")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "hello")] HttpRequestData req,
FunctionContext executionContext)
{
var acceptedScopes = new[] { "read:messages", "write:messages" };
var (isAuthenticated, response) = await _jwtService.AuthenticateAndAuthorizeAsync(req, executionContext, acceptedScopes);
if (!isAuthenticated)
{
return response;
}
var okResponse = req.CreateResponse(HttpStatusCode.OK);
await okResponse.WriteStringAsync("Hello, world!");
return okResponse;
}
}
}
and do not forget to add your service in startup function host builder
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<JwtService>();
}
Hope this helps!