When I create a new asp.net mvc 4.0 application, one of the first thing I do, is create and set a custom authorize global filter
like so:
//FilterConfig.cs public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //filters.Add(new HandleErrorAttribute()); filters.Add(new CustomAuthorizationAttribute()); }
Then I create the CustomAuthorizationAttribute
like so:
//CustomAuthorizationAttribute.cs protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { if (filterContext.HttpContext.Request.IsAjaxRequest()) { //Handle AJAX requests filterContext.HttpContext.Response.StatusCode = 403; filterContext.Result = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } else { //Handle regular requests base.HandleUnauthorizedRequest(filterContext); //let FormsAuthentication make the redirect based on the loginUrl defined in the web.config (if any) } }
I have two controllers: HomeController
and SecureController
The HomeController is decorated with the [AllowAnonymous]
attribute.
The SecureController is NOT decorated with the [AllowAnonymous]
attribute.
The Index() ActionResult
of the HomeController
displays a View with a simple button.
When I click the button, I make an ajax call to a GetData() method that lives inside the SecureController
like so:
$("#btnButton").click(function () { $.ajax({ url: '@Url.Action("GetData", "Secure")', type: 'get', data: {param: "test"}, success: function (data, textStatus, xhr) { console.log("SUCCESS GET"); } }); });
Needless to say, when I click the button, I trigger the CustomAuthorizationAttribute
because it is a global filter but also because the SecureController
is NOT decorated with the [AllowAnonymous]
attribute.
Ok, I’m done with my introduction...
With the introduction of asp.net mvc 5.0
, we are now introduced to a new authentication filter
which happens to get triggered before the authorization filter (which is great and gives us more granular control on how I can differentiate a user that is NOT authenticated (http 401) from a user that IS authenticated and who happens to NOT be authorized (http 403)).
In order to give this new authentication filter
a try, I’ve created a new asp.net mvc 5.0 (VS Express 2013 for Web) and started by doing the following:
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //filters.Add(new HandleErrorAttribute()); filters.Add(new CustomAuthenticationAttribute()); //Notice I'm using the word Authentication and not Authorization }
Then the attribute:
public class CustomAuthenticationAttribute : ActionFilterAttribute, IAuthenticationFilter { public void OnAuthentication(AuthenticationContext filterContext) { } public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) { var user = filterContext.HttpContext.User; if (user == null || !user.Identity.IsAuthenticated) { filterContext.Result = new HttpUnauthorizedResult(); } } }
I’ve created a HomeController
. The HomeController
is decorated with the [AllowAnonymous]
attribute.
Before launching the application from VS 2013, I’ve set two break points inside both methods of my CustomAuthenticationAttribute (OnAuthentication
and OnAuthenticationChallenge
).
When I launch the application, I hit the first break point(OnAuthentication
). Then, to my surprise, the code within the Index() ActionResult
of my HomeController
gets executed and only after I return the View() do I hit the break point on the OnAuthenticationChallenge()
method.
Questions: I have two questions.
Question 1)
I was under the impression that the [AllowAnonymous]
attribute would automagically bypass any code within my CustomAuthenticationAttribute
but I was wrong! Do I need to manually check for the existence of the [AllowAnonymous]
attribute and skip any code?
Question 2) Why is the code inside my Index()
method of my HomeController
gets executed after the OnAuthentication
? Only to realize that after I return View() do the code inside the OnAuthenticationChallenge()
gets executed?
My concern is that I do not want the code from the Index()
method to get executed if the user is NOT authenticated.
Perhaps I’m looking at this the wrong way.
If anyone can help me shed some light on this, that’d be great!
Sincerely Vince