1

We use an IAuthorizationFilter class to filter all request, and check if custom user claims are still present (multi-tenant app) in the authentication cookie. These information are essentials for the rest of the application. If these informations are not present, we redirect to the Login page.

    public class TokenAuthorizationFilter : IAuthorizationFilter, IAsyncAuthorizationFilter
    {

        public TokenAuthorizationFilter()
        {
            // Some dependency injection ...
        }

        public void OnAuthorization(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
        {
            CheckToken(context);
        }

        public Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
        {
            return CheckToken(context);
        }
}

And we register our filter like this

    services.AddMvc(config =>
    {
        config.Filters.Add(typeof(TokenAuthorizationFilter));
    });

And the controller's action that I want to access is very simple :

[Authorize(Policy = "TokenValid")]
public class HomeController : AjaxBaseController
{
    public IActionResult Index()
    {
        return View();
    }
}

We even not reached the Policy of our AuthorizeAttribute. As I can see in the stacktrace, Identity is attempting to create a Microsoft.AspNet.Identity.SignInManager somewhere in the middleware after checking for a CookieAuthenticationOptions, I assumed that he's attempting to re-login the user, but it's not checking for my Filter ? Login is very special in our application, so I don't want to let Identity log automatically our user. I can reproduced this issue when the authentication cookie expired. Any ideas ? Thanks !

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Christophe Gigax
  • 3,211
  • 4
  • 25
  • 37

2 Answers2

5

You also need to make TokenAuthorizationFilter inherit from AuthorizeAttribute for an authorization filter, and rename it as TokenAuthorizationFilterAttribute. This will become an attribute that you will be able to call with [TokenAuthorizationFilter]:

[TokenAuthorizationFilter]
public class HomeController : AjaxBaseController
{
    public IActionResult Index()
    {
        return View();
    }
}

Be careful when implementing both IAuthorizationFilter and IAsyncAuthorizationFilter, as ASP.NET Core will only call the async method in this case: if you do not need any async call, then only implement the IAuthorizationFilter interface.

Also, if you keep to register the filter like this:

services.AddMvc(config =>
{
    config.Filters.Add(typeof(TokenAuthorizationFilter));
});

You will notice that the filter will be called for every action, as it will force the authorization filter to be called every time, so in this case you do not need to add the attribute on top of your action.

Chris G.
  • 79
  • 1
  • 6
  • I've read that inherit from `AuthorizeAttribute` is not a good solution, better use Policy for that. – Christophe Gigax Jul 07 '16 at 07:10
  • Agreed, but in this case you do not even need an IAuthorizationFilter, you just need to add policies with custom (or not) requirements when configuring your services in the Startup.cs class `services.AddAuthorization(options => options.AddPolicy("PolicyName", policy => { you add your requirements here }))` – Chris G. Jul 07 '16 at 14:09
  • Yes you've right. We moved the validation of the token from `IAuthorizationFilter` to `CookieAuthenticationEvents`, that worked better. Thanks for you help ! – Christophe Gigax Jul 07 '16 at 15:29
1

Finally I found out the problem. Every 30 minutes, Identity is trying to validate the user through SecurityStamp validation, and that's making the app crash because it needed a database connection which doesn't exists at the time of the validation. We've desactive this validation in our startup by reimplementing the OnValidatePrincipal :

options.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents { OnValidatePrincipal = (context) => Task.FromResult(0) };
Christophe Gigax
  • 3,211
  • 4
  • 25
  • 37