ASP.NET MVC 5 Authentication Filters

Posted by: Raj Aththanayake , on 11/21/2013, in Category DNC Magazine
Views: 123217
Abstract: Explore the new IAuthenticationFilter in ASP.NET MVC 5 which allows you to customize authentication. Also learn about the CustomAuthentication attribute and how you can use to change the current principal and redirect un authenticated user to a login page.

ASP.NET MVC 5 has some great improvements around authentication. This includes new Authentication filters, new Authentication options and ASP.NET Identity Management.

In this article, we will take a look at the new authentication filters and how you can use these filters to make authentication decisions. At this stage, ASP.NET MVC does not provide any built-in authentication filter(s). However it provides you with the framework, so you can easily create your own custom authentication filters.

If you have used ASP.NET MVC before, you probably have used AuthorizationFilters. Authorization filters allow you to perform authorization tasks for an authenticated user. A good example is Role based authorization.

ASP.NET MVC 4 also introduced a built-in AllowAnonymous attribute. This attribute allows anonymous users to access certain Controllers/Actions. This way, you can protect the entire site by using this Authorize attribute and then use the AllowAnonymous attribute, to allow anonymous users to access certain Actions and Controllers. If you would like to read more on AllowAnonymous attribute, here’s a good article http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx

New authentication filters run prior to authorization filters. It is also worth noting that these filters are the very first filters to run before any other filters get executed.

This article is published from the DotNetCurry .NET Magazine – A Free High Quality Digital Magazine for .NET professionals published once every two months. Subscribe to this eMagazine for Free and get access to hundreds of free tutorials from experts

 

So what is the real reason behind Authentication filters?

Prior to authentication filters, developers used the Authorization filters to drive some of the authentication tasks for the current request. It was convenient because the Authorization filters were executed prior to any other action filters.  For example, before the request routes to action execution, we would use an Authorization filter to redirect an unauthenticated user to a login page. Another example would be to use the Authorization filter to set a new authentication principal, which is different from the application’s original principal in context.

Authentication related tasks can now be separated out to a new custom authentication filter and authorization related tasks can be performed using authorization filters. So it is basically about separating of concerns, while giving developers more flexibility to drive authentication using ASP.NET MVC infrastructure.

Authentication Filters as your standard Action Filters

As you would normally do with Action Filters, you can apply Authentication logic per Action, per Controller or Globally for all Controllers.

A practical example would be where your entire site runs on Forms based authentication using cookies, but you have a special requirement to support certain Controllers/Actions that should be transparent to a different authentication provider. This authentication provider would issue tokens along with the claim based authentication. Certain Controllers would have access to these claims principals. You can use a custom Authentication filter to set the new principal (i.e claims based), for the current request, just for the Controllers/Actions we need. The rest of the site will continue to work with the existing forms based authentication.

Creating a new custom Authentication Filter with ASP.NET MVC 5

We will first create a new ASP.NET MVC 5 application. Once you have installed Visual Studio 2013, navigate to File > New Project, under templates (C#/VB) select Web, and then select ASP.NET Web application. A dialogue box will pop up. Select “MVC” from the template. Then simply click on “Create Project”.

mvc5-new-project

In order to create an Authentication filter, you must implement the IAuthenticationFilter. Below is the implementation of ASP.NET MVC’s IAuthenticationFilter.

public interface IAuthenticationFilter
{
    void OnAuthentication(AuthenticationContext filterContext);

    void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext);
}

As I mentioned earlier, to be able to use these filters as your standard Action Filters, you also need to derive from ActionFilterAttribute. This allows you to decorate your Authentication filter in Controllers, Actions or use as a Global filter.

public class CustomAuthenticationAttribute : ActionFilterAttribute, IAuthenticationFilter
{
    public void OnAuthentication(AuthenticationContext filterContext)
    {           
    }

    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {           
    }
}

If you look at the above CustomAuthenticationAttribute, there are two interesting methods required to implement.

a. OnAuthentication

b. OnAuthenticationChallenge.

Let’s take a look at these two methods in detail.

OnAuthentication

During Action invocation, ASP.NET MVC invokes Authentication Filters by calling the following method:

AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);

This method creates an AuthenticationContext using the original principal, and executes each filter’s OnAuthentication method. Please see the following code:

AuthenticationContext context = new AuthenticationContext(controllerContext, actionDescriptor, originalPrincipal);

foreach (IAuthenticationFilter filter in filters)
{
      filter.OnAuthentication(context);
}

AuthenticationContext provides you information for performing authentication. You can use this information to make authentication decisions based on the current context. For example, you may decide to modify the ActionResult to different result type based on the authentication context, or you may decide to change the current principal based on the authentication context etc.

If you change the user’s current principal to use a new custom principal, the ASP.NET MVC framework will set a new principal as shown here:

IPrincipal newPrincipal = context.Principal;

if (newPrincipal != originalPrincipal)
{
      Contract.Assert(context.HttpContext != null);
      context.HttpContext.User = newPrincipal;
      Thread.CurrentPrincipal = newPrincipal;
}

OnAuthenticationChallenge

OnAuthenticationChallenge method runs after the OnAuthentication method. You can use OnAuthenticationChallenge method to perform additional tasks on the request.

protected virtual AuthenticationChallengeContext InvokeAuthenticationFiltersChallenge(
ControllerContext controllerContext, IList<IAuthenticationFilter> filters,
ActionDescriptor actionDescriptor, ActionResult result)


Similar to AuthenticationContext, this method also creates an AuthenticationChallengeContext as shown here. Then it invokes the OnAuthenticationChallenge method per AuthenticationFilter.

AuthenticationChallengeContext context = new AuthenticationChallengeContext(controllerContext,
actionDescriptor, result);

foreach (IAuthenticationFilter filter in filters)
{
filter.OnAuthenticationChallenge(context);
}

The key thing to remember is that OnAuthenticationChallenge does not necessarily run before every other Action Filter. It can run at various stages. For example, it can run after AuthorizationFilters or it can run after Action execution completed so on. Since this is at various stages, you now have the ability to change the action result based on the authentication.

As I mentioned earlier, you can use the OnAuthenticationChallenge method to modify the ActionResult.

filterContext.Result = new HttpUnauthorizedResult();

Implementing and configuring your custom Authentication Filter

We will use the CustomAuthenticationAttribute we just created and implement both OnAuthentication and OnAuthenticationChallenge methods. We will then configure it to execute only for HomeController’s Index action.

In OnAuthentication we set the current principal to a custom principal with some additional properties. In OnAuthenticationChallenge we check whether the user is authenticated or not and modify the ActionResult to an HttpUnAuthorizedResult.

CustomAuthenticationAttribute:

public class CustomAuthenticationAttribute : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext) {
   
    //For demo purpose only. In real life your custom principal might be retrieved via different source. i.e context/request etc.
    filterContext.Principal = new MyCustomPrincipal(filterContext.HttpContext.User.Identity, new []{"Admin"}, "Red");
}

public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) {
    var color = ((MyCustomPrincipal) filterContext.HttpContext.User).HairColor;
    var user = filterContext.HttpContext.User;
   
    if (!user.Identity.IsAuthenticated)
    {
        filterContext.Result = new HttpUnauthorizedResult();
    }
}
}

HomeController:

Here we execute the CustomAuthenticationAttribute only for HomeController’s Index Action. By configuring the CustomAuthenticationAttribute this way, we have the ability to control authentication just for Index action.

public class HomeController : Controller
{
    [CustomAuthentication]
    public ActionResult Index()
    {
        return View();
    }
}

OnAuthentication implementation will modify the current principal to use the new MyCustomPrincipal. During the Index action, we will use the new MyCustomPrincipal. Any other Action would use the original principal.

For example, accessing the MyCustomPrincipal from any other Action other than Index Action would not be possible. The following code would throw an exception:

public ActionResult About()
{
    var color = ((MyCustomPrincipal)ControllerContext.HttpContext.User).HairColor;

    return View();
}

OnAuthenticationChallenge method sets the ActionResult as HttpUnAuthorizedResult for un- authenticated users. This causes user to redirect to a login page.

Additional Customizations

You probably know that the ASP.NET MVC Controller itself is also an ActionFilter.

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{ }

With the introduction of IAuthenticationFilter, you can customize your authentication within the Controller as shown here:

public class HomeController : Controller
{
    protected override void OnAuthentication(AuthenticationContext filterContext)
    {
        //custom authentication logic
    }

    protected override void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
        //custom authentication challenge logic
    }
}

Summary

The new IAuthenticationFilter provides a great ability to customize authentication within an ASP.NET MVC 5 application. This provides a clear separation between authentication and authorization filters. OnAuthentication and OnAuthenticationChallenge methods provide greater extensibility points to customize authentication within ASP.NET MVC framework. We also looked at a sample usage of CustomAuthentication attribute and how you can use to change the current principal and redirect un authenticated user to a login page.

Download the entire source code from our GitHub Repository at bit.ly/dncm9-mvc5auth

Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Raj Aththanayake is a Microsoft ASP.NET Web Developer specializing in Agile Development practices such Test Driven Development (TDD) and Unit Testing. He is also passionate in technologies such as ASP.NET MVC. He regularly presents at community user groups and conferences. Raj also writes articles in his blog http://blog.rajsoftware.com. You can follow Raj on twitter @raj_kba


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Andrzej on Thursday, November 21, 2013 1:17 AM
Brilliant article Raj. Please tell about other new features also
Comment posted by Shravan on Monday, December 2, 2013 8:07 AM
how exactly the new auth filter are useful other than changing principal?
Comment posted by Assil Abdulrahim on Tuesday, February 11, 2014 10:27 AM
Such nice and clear.
Thanks a lot!
Comment posted by Bill on Tuesday, March 18, 2014 2:49 PM
I agree with Shravan...that is, what's the point?  I mean, your article is good and provides some interesting, new info, but when and how and why would you use this in the "real world," for a "real" production application?

Would you be able to expand on your post and give a real production-quality example?

For me specifically, our ASP.NET MVC 5 app has to use Windows authentication but still save the user and a lot of other user-related data to the database, so would this filter help with that scenario?
Comment posted by Chen Noam on Monday, April 28, 2014 4:24 PM
Hi Raj,

First thank you for the time writing this great article.
I've looked for a solution. My issue was as follows:
My application is separated into two parts: client website (HTML 5, CSS3 and AngularJS) and server webservices (MVC).
I wanted my MVC (no views) will return JSON data only, when a user is not authenticated the server retrieves 401 (unauthorized), in the client side I've http intercepetor that will listen to 401 and will redirect (client wise) to client login page.
Everytime I tried to disable MVC automatic redirect to login page it didn't help, until I've used the filter authentication filter (BTW I know the Authorization filter from MVC4). In the beginning it didn't work since the Forms authentication is working be default, so I switched it off in the web.config and now it's working perfectly.

I can answer several of the questions above, I use it because I needed to validate the users with my database using my users table in an easy way without using the member ship complicated tables, I wanted to build my own easy authentication.

Hope it will help someone.

Best regards,
Chen
Comment posted by Chen Noam on Monday, April 28, 2014 6:57 PM
Hi Raj again,

I'm sorry for misleading you.
I've found the issue, my code used an older mvc and webpages version.
When moving to MVC 5 and webpages 3, working as expected with custom filter and authentication mode = Forms.

Thanks again,
Chen
Comment posted by Chen Noam on Monday, April 28, 2014 7:15 PM
In my case I didn't need to use the custom principal, I just wanted to response 401 on unauthorized situation.
I've created SignIn method that checked the user and called FormsAuthentication.SetAuthCookie(username, false);
to keep the user cookie (same in SignUp).

In order to make it work you need to add in the OnAuthenticationChallenge:
if(!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
    filterContext.Result = new HttpUnauthorizedResult();
    filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}

Sorry for many comments :)
Chen
Comment posted by ret on Wednesday, May 7, 2014 6:40 AM
dfsdf
Comment posted by ccccc on Tuesday, October 14, 2014 1:22 AM
sadds

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel