I have a .NET 6.0 isolated azure function app. I want to implement custom JWT token validation. I am following this blog and created Authentication middleware for validating tokens. However there is a piece of code which uses reflection to get target function information in the middleware. This blog is almost 2 years old now. Is there any better way to do it now? I wasn't able to find anything on my own.
Middleware class:
public class AuthenticationMiddleware : IFunctionsWorkerMiddleware
{
private readonly JwtSecurityTokenHandler _tokenValidator;
private readonly TokenValidationParameters _tokenValidationParameters;
public AuthenticationMiddleware()
{
_tokenValidator = new JwtSecurityTokenHandler();
_tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Environment.GetEnvironmentVariable("JWTIssuer"),
ValidAudience = Environment.GetEnvironmentVariable("JWTAudience"),
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWTSecretKey")))
};
}
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
var targetMethod = GetTargetFunctionMethod(context);
var attributes = GetFunctionMethodAttribute<AuthorizeAttribute>(targetMethod);
if (attributes != null && attributes.Count > 0)
{
//this section should be executed only if [Authorize] attribute is declared on Function
//and will return 401-Unauthorized response code if token is invalid
await next(context);
}
else
{
//anonymous access allowed
await next(context);
}
}
private static List<T> GetFunctionMethodAttribute<T>(MethodInfo targetMethod) where T : Attribute
{
var methodAttributes = targetMethod.GetCustomAttributes<T>();
var classAttributes = targetMethod.DeclaringType.GetCustomAttributes<T>();
return methodAttributes.Concat(classAttributes).ToList();
}
private static MethodInfo GetTargetFunctionMethod(FunctionContext context)
{
var assemblyPath = context.FunctionDefinition.PathToAssembly;
var assembly = Assembly.LoadFrom(assemblyPath);
var typeName = context.FunctionDefinition.EntryPoint.Substring(0, context.FunctionDefinition.EntryPoint.LastIndexOf('.'));
var type = assembly.GetType(typeName);
var methodName = context.FunctionDefinition.EntryPoint.Substring(context.FunctionDefinition.EntryPoint.LastIndexOf('.') + 1);
var method = type.GetMethod(methodName);
return method;
}
}
Attribute class:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeAttribute : Attribute
{
public string[] UserRoles { get; set; } = Array.Empty<string>();
}
HttpTrigger functions:
[Function("AllowAnonymous")]
public static HttpResponseData AllowAnonymous([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req, FunctionContext context)
{
var response = req.CreateResponse(HttpStatusCode.OK);
response.WriteString("AllowAnonymous succeeded");
return response;
}
[Authorize]
[Function("AllowAuthenticatedOnly")]
public static HttpResponseData AllowAuthenticatedOnly([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req, FunctionContext context)
{
var response = req.CreateResponse(HttpStatusCode.OK);
response.WriteString("AllowAuthenticatedOnly succeeded");
return response;
}
Program class:
public class Program
{
public static void Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
builder.UseMiddleware<AuthenticationMiddleware>();
})
.Build();
host.Run();
}
}
You can see that GetTargetFunctionMethod is being called to get target method information, which gets info using reflection.