Reputation: 7433
On my site I have a few controller
s that are restricted to authenticated users. Beyond that, there are a few of those controllers that require authentication, that ALSO need to be restricted based on the time of year.
To handle that, I created a TimeRangeFilter
ActionFilter
/ResourceFilter
.
Here's what it looks like:
public class TimeRangeFilter : Attribute, IActionFilter, IOrderedFilter
{
public string AllowedMonths { get; set; } // like |3|4|5|
public string RedirectUrl { get; set; }
...... // OnActionExecuting....
}
Then, on my Controller
's class
, I implement like this:
[TimeRangeFilter(AllowedMonths = "|3|4|", RedirectUrl = "/feature/welcome", Order = 1)]
[Authorize]
[IsNotNefarious]
public class HubController : BaseController
{...}
But, even with the IOrderedFilter
interface
on the filter
, the AuthorizationFilter
executes first, THEN my TimeRangeFilter
.
For this welcome page, I don't want to require the user to be logged in to see it. But I don't want to have to change the URL that gets to my Hub page based on those allowed months.
How do I prioritize my ActionFilter
/ResourceFilter
to execute, and short-circuit, prior to the AuthorizationFilter
execution?
Upvotes: 3
Views: 4079
Reputation: 91
The solution from @Alexander does no longer work in .NET Core 3.1; now the Authorize
attribute is being evaluated in the AuthorizationMiddleware
, many steps before the filters are hit.
The best new approach is to make a custom middleware yourself, insert it after UseRouting();
in startup, and let it query the endpoint information manually. Example:
public class TimeRangeAttribute : Attribute {
public string Info { get; set; }
}
public class TimeRangeMiddleware {
private readonly RequestDelegate _next;
public TimeRangeMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context) {
var endpoint = context.GetEndpoint();
if (endpoint?.Metadata.GetMetadata<TimeRangeAttribute>() != null) {
// check allowed or not
}
if(_next != null) await _next(context);
}
}
// In Startup
public void Configure(...) {
// ....
app.UseRouting();
app.UseMiddleware<TimeRangeMiddleware>();
app.UseAuthentication();
app.UseAuthorization();
// ...
}
Upvotes: 5
Reputation: 9632
Short answer is "You can't make ActionFilter
execute before AuhtorizeFilter
". But you can turn TimeRangeFilter
into authorization filter
public class TimeRangeFilterAttribute : Attribute, IAuthorizationFilter, IOrderedFilter
{
public string AllowedMonths { get; set; } // like |3|4|5|
public string RedirectUrl { get; set; }
public void OnAuthorization(AuthorizationFilterContext context)
{
if (not allowed) {
context.Result = new RedirectResult(RedirectUrl);
}
}
}
Specify Order = 0
to make it run before other Authorize
checks, or try not implement IOrderedFilter
for it and it will be executed first as well.
Upvotes: 3