Coke
Coke

Reputation: 985

Injecting IOptions<> into ApiKeyAuthorizeAttribute

I am using options pattern that stores different configurations, including API keys for different environments. So far I have been using it fine and injecting my values into classes as needed.

However, I faced a little challenge while trying to setup authorization in the controller and run validation against my ApiKey that is unique per environment, because I was not able to inject IOptions into ApiKeyAuthorizeAttribute class to perform validation.

Here is how my controller looks like now:

[ApiKeyAuthorize]
public class NotificationSettingsController : Controller
{
    //some endpoints here
}

ApiKeyAuthorize Class:

public class ApiKeyAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    //////This...
    private readonly IOptions<MyConfig> _config;

    public ApiKeyAuthorizeAttribute(IOptions<MyConfig> config)
    {
        _config = config;
    }
    /////////...is what I am trying to accomplish
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var request = context.HttpContext.Request;

        var foundApiKeys = request.Headers.TryGetValue("ReplaceWithOptionsApiKeyName", out var requestApiKeys);

        if (!foundApiKeys || requestApiKeys[0] != "ReplaceWithOptionsApiKeyValue")
        {
            context.Result = new UnauthorizedResult();
        }
    }
}

My problem is that injecting here isn't possible, but I need to get a value from IOptions<> to run ApiKey validation.

Upvotes: 1

Views: 703

Answers (1)

Chris Pratt
Chris Pratt

Reputation: 239240

Attributes are constructed in-place, so it's not possible to inject dependencies into them. However, ASP.NET Core provides a workaround. Instead of applying the attribute directly, you can use the ServiceFilter attribute instead and pass it the type of the filter you want to apply:

[ServiceFilter(typeof(ApiAuthorizeAttribute))]

This will dynamically apply the filter to the controller/action while instantiating it with any dependencies it requires at the same time. However, it does limit you in the other direction. For example, if you need to do something like:

[ApiAuthorizeAttribute(Roles = "Admin")]

It would not be possible to achieve this with the ServiceFilter attribute, because you cannot pass property values, like Roles here, along with the type.

Upvotes: 3

Related Questions