4

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.

2
  • I have hit the same issue on .NET 8, and I have spend most of the day looking for a solution and I am not able to find any so I think that this is only solution so far for this problem. Commented Apr 16, 2024 at 19:50
  • If you are using Isolated mode with ASP.NET Core integration, you can use DarkLoop.Azure.Functions.Authorization which has all this functionality built-in using ASP.NET cores auth modules. You would have to ensure you are using ASP.NET Core integration for Isolated mode. More on this article. Commented Jul 26, 2024 at 14:23

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.