Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get user and claim information using action filters?

Right now I am doing this to get the information I need:

In my base controller:

    public int roleId { get; private set; }     public int userId { get; private set; }      public void setUserAndRole()     {         ClaimsIdentity claimsIdentity;         var httpContext = HttpContext.Current;         claimsIdentity = httpContext.User.Identity as ClaimsIdentity;         roleId = Int32.Parse(claimsIdentity.FindFirst("RoleId").Value);         userId = Int32.Parse(User.Identity.GetUserId());     } 

In my controller methods:

    public async Task<IHttpActionResult> getTest(int examId, int userTestId, int retrieve)     {         setUserAndRole(); 

I wanted the roleId and userId to be available and populated in the constructor of my class but from what I understand the constructor fires before authorization information is available.

Can someone tell me how I could do this with an Action Filter? Ideally I would like the Action Filter to be at the controller level but if not then could it be done at the method level.

I am hoping for some good advice and suggestions. Thank you

Update to show System.Web.Http

#region Assembly System.Web.Http, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 // C:\H\server\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll #endregion  using System.Threading; using System.Threading.Tasks; using System.Web.Http.Controllers;  namespace System.Web.Http.Filters {     //     // Summary:     //     Represents the base class for all action-filter attributes.     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]     public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IFilter     {         //         // Summary:         //     Initializes a new instance of the System.Web.Http.Filters.ActionFilterAttribute         //     class.         protected ActionFilterAttribute();          //         // Summary:         //     Occurs after the action method is invoked.         //         // Parameters:         //   actionExecutedContext:         //     The action executed context.         public virtual void OnActionExecuted(HttpActionExecutedContext actionExecutedContext);         public virtual Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken);         //         // Summary:         //     Occurs before the action method is invoked.         //         // Parameters:         //   actionContext:         //     The action context.         public virtual void OnActionExecuting(HttpActionContext actionContext);         public virtual Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken);     } } 
like image 707
Alan2 Avatar asked Mar 03 '16 06:03

Alan2


People also ask

How do you use action filters?

Using an Action Filter An action filter is an attribute. You can apply most action filters to either an individual controller action or an entire controller. For example, the Data controller in Listing 1 exposes an action named Index() that returns the current time.

How do I get an action filter model?

In case your controller action has multiple arguments and in your filter you want to select the one that is bound via [FromBody] , then you can use reflection to do the following: public void OnActionExecuting(ActionExecutingContext context) { foreach (ControllerParameterDescriptor param in context. ActionDescriptor.

What is the order in which the action filters are executed?

Filters execute in this order: Authorization filters. Action filters. Response/Result filters.


1 Answers

Based on your method signature (and later comments below) the code assumes that you are using Web API and not MVC although this could easily be changed for MVC as well.

I do want to specify that if you look purely at the requirements its how can I create a maintainable piece of code that is reused. In this case the code gets claims based information and injects it into your controllers. The fact that you are asking for a Filter is a technical requirement but I am also going to present a solution that does not use a Filter but an IoC instead which adds some flexibility (IMHO).

Some Tips

  • Try to always use interfaces when/where possible. It makes for easier unit testing, easier to alter the implementation, etc. I will not go into that all here but here is a link.
  • In WebAPI and also MVC do not use the System.Web.HttpContext.Current. It is very hard to unit test code that makes use of this. Mvc and Web API have a common abstraction called HttpContextBase, use this when possible. If there is no other way (I have not seen this yet) then use new HttpContextWrapper(System.Web.HttpContext.Current) and pass this instance in to what ever method/class you want to use (HttpContextWrapper derives from HttpContextBase).

Proposed Solutions

These are in no particular order. See end for a basic pro list of each solution.

  1. Web API Filter - exactly what you are asking for. A Web API action filter to inject the claims based information into your Web Api methods.
  2. IoC/DI - A very flexible approach to injecting dependencies into your Controllers and classes. I used AutoFac as the Di framework and illustrate how you can get the claims based info injected into your controller.
  3. Authorization Filter - Essentially an extension on solution 1 but used in a manner in which you can secure access to your Web API interface. As it was not clear how you wanted to use this information I made the jump in this proposal that you wanted it to ensure the user had sufficient privileges.

Common Code

UserInfo.cs

This is common code used in both solutions that I will demo below. This is a common abstraction around the properties / claims based info you want access to. This way you do not have to extend methods if you want to add access to another property but just extend the interface / class.

using System; using System.Security.Claims; using System.Web; using Microsoft.AspNet.Identity;  namespace MyNamespace {     public interface IUserInfo     {         int RoleId { get; }         int UserId { get; }         bool IsAuthenticated { get; }     }      public class WebUserInfo : IUserInfo     {         public int RoleId { get; set; }         public int UserId { get; set; }         public bool IsAuthenticated { get; set; }          public WebUserInfo(HttpContextBase httpContext)         {             try             {                 var claimsIdentity = httpContext.User.Identity as ClaimsIdentity;                 IsAuthenticated = httpContext.User.Identity.IsAuthenticated;                 if (claimsIdentity != null)                 {                     RoleId = Int32.Parse(claimsIdentity.FindFirst("RoleId").Value);                     UserId = Int32.Parse(claimsIdentity.GetUserId());                 }             }             catch (Exception ex)             {                 IsAuthenticated = false;                 UserId = -1;                 RoleId = -1;                  // log exception             }          }     } } 

Solution 1 - Web API Filter

This solution demos what you asked for, a reusable Web API filter that populates the claims based info.

WebApiClaimsUserFilter.cs

using System.Web; using System.Web.Http.Controllers;  namespace MyNamespace {     public class WebApiClaimsUserFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute     {         public override void OnActionExecuting(HttpActionContext actionContext)         {             // access to the HttpContextBase instance can be done using the Properties collection MS_HttpContext             var context = (HttpContextBase) actionContext.Request.Properties["MS_HttpContext"];             var user = new WebUserInfo(context);             actionContext.ActionArguments["claimsUser"] = user; // key name here must match the parameter name in the methods you want to populate with this instance             base.OnActionExecuting(actionContext);         }     } } 

Now you can use this filter by applying it to your Web API methods like an attribute or at the class level. If you want access everywhere you can also add it to the WebApiConfig.cs code like so (optional).

public static class WebApiConfig {     public static void Register(HttpConfiguration config)     {         config.Filters.Add(new WebApiClaimsUserFilterAttribute());         // rest of code here     } } 

WebApiTestController.cs

Here how to use it in a Web API method. Note that the matching is done based on the parameter name, this has to match the name assigned in the filter actionContext.ActionArguments["claimsUser"]. Your method will now be populated with the added instance from your filter.

using System.Web.Http; using System.Threading.Tasks;  namespace MyNamespace {     public class WebApiTestController : ApiController     {         [WebApiClaimsUserFilterAttribute] // not necessary if registered in webapiconfig.cs         public async Task<IHttpActionResult> Get(IUserInfo claimsUser)         {             var roleId = claimsUser.RoleId;             await Task.Delay(1).ConfigureAwait(true);             return Ok();         }     } } 

Solution 2 - IoC / DI

Here is a wiki on Inversion of Control and a wiki on Dependency Injection. These terms, IoC and DI, are usually used interchangeably. In a nutshell you define dependencies, register them with a DI or IoC framework, and these dependency instances are then injected in your running code for you.

There are many IoC frameworks out there, I used AutoFac but you can use whatever you want. Following this method you define your injectibles once and get access to them wherever you want. Just by referencing my new interface in the constructor it will be injected with the instance at run time.

DependencyInjectionConfig.cs

using System.Reflection; using System.Web.Http; using System.Web.Mvc; using Autofac; using Autofac.Integration.Mvc; using Autofac.Integration.WebApi;  namespace MyNamespace {     public static class DependencyInjectionConfig     {         /// <summary>         /// Executes all dependency injection using AutoFac         /// </summary>         /// <remarks>See AutoFac Documentation: https://github.com/autofac/Autofac/wiki         /// Compare speed of AutoFac with other IoC frameworks: http://nareblog.wordpress.com/tag/ioc-autofac-ninject-asp-asp-net-mvc-inversion-of-control          /// </remarks>         public static void RegisterDependencyInjection()         {             var builder = new ContainerBuilder();             var config = GlobalConfiguration.Configuration;              builder.RegisterApiControllers(Assembly.GetExecutingAssembly());              builder.RegisterControllers(typeof(DependencyInjectionConfig).Assembly);              builder.RegisterModule(new AutofacWebTypesModule());              // here we specify that we want to inject a WebUserInfo wherever IUserInfo is encountered (ie. in a public constructor in the Controllers)             builder.RegisterType<WebUserInfo>()                 .As<IUserInfo>()                 .InstancePerRequest();              var container = builder.Build();             // For Web API             config.DependencyResolver = new AutofacWebApiDependencyResolver(container);              // 2 lines for MVC (not web api)             var resolver = new AutofacDependencyResolver(container);             DependencyResolver.SetResolver(resolver);         }     } } 

Now we just have to call this when our application starts, this can be done in the Global.asax.cs file.

using System; using System.Web; using System.Web.Mvc; using System.Web.Routing; using System.Web.Http;  namespace MyNamespace {     public class Global : HttpApplication     {         void Application_Start(object sender, EventArgs e)         {             DependencyInjectionConfig.RegisterDependencyInjection();             // rest of code         }     } } 

Now we can use it where ever we want.

WebApiTestController.cs

using System.Web.Http; using System.Threading.Tasks;  namespace MyNamespace {     public class WebApiTestController : ApiController     {         private IUserInfo _userInfo;         public WebApiTestController(IUserInfo userInfo)         {             _userInfo = userInfo; // injected from AutoFac         }         public async Task<IHttpActionResult> Get()         {             var roleId = _userInfo.RoleId;             await Task.Delay(1).ConfigureAwait(true);             return Ok();         }     } } 

Here are the dependencies you can get from NuGet for this example.

Install-Package Autofac Install-Package Autofac.Mvc5 Install-Package Autofac.WebApi2 

Solution 3 - Authorization Filter

One more solution I thought of. You never specified why you needed the user and role id. Maybe you want to check access level in the method before proceeding. If this is the case the best solution is to not only implement a Filter but to create an override of System.Web.Http.Filters.AuthorizationFilterAttribute. This allows you to execute an authorization check before your code even executes which is very handy if you have varying levels of access across your web api interface. The code I put together illustrates the point but you could extend it to add actual calls to a repository for checks.

WebApiAuthorizationClaimsUserFilterAttribute.cs

using System.Net; using System.Net.Http; using System.Web; using System.Web.Http.Controllers;  namespace MyNamespace {     public class WebApiAuthorizationClaimsUserFilterAttribute : System.Web.Http.Filters.AuthorizationFilterAttribute     {         // the authorized role id (again, just an example to illustrate this point. I am not advocating for hard coded identifiers in the code)         public int AuthorizedRoleId { get; set; }          public override void OnAuthorization(HttpActionContext actionContext)         {             var context = (HttpContextBase) actionContext.Request.Properties["MS_HttpContext"];             var user = new WebUserInfo(context);              // check if user is authenticated, if not return Unauthorized             if (!user.IsAuthenticated || user.UserId < 1)                 actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "User not authenticated...");             else if(user.RoleId > 0 && user.RoleId != AuthorizedRoleId) // if user is authenticated but should not have access return Forbidden                 actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden, "Not allowed to access...");         }     } } 

WebApiTestController.cs

using System.Web.Http; using System.Threading.Tasks;  namespace MyNamespace {     public class WebApiTestController : ApiController     {         [WebApiAuthorizationClaimsUserFilterAttribute(AuthorizedRoleId = 21)] // some role id         public async Task<IHttpActionResult> Get(IUserInfo claimsUser)         {             // code will only be reached if user is authorized based on the filter             await Task.Delay(1).ConfigureAwait(true);             return Ok();         }     } } 

Quick Comparison of Solutions

  • If you want flexibility go with AutoFac. You can reuse this for many of the moving parts of your solution/project. It makes for very maintainable and testable code. You can extend it very easily once its setup and running.
  • If you want something static and simple that is guaranteed not to change and you have minimal number of moving parts where an DI framework would be overkill then go with the Filter solution.
  • If you want to execute authorization checks in a single location then a custom AuthorizationFilterAttribute is the best way to go. You can add the code from the filter in solution #1 to this code if authorization passes, this way you still have access to the user information for other purposes in your code.

Edits

  • I added a 3rd solution to the list of possibilities.
  • Added a solution summary at the top of the answer.
like image 193
Igor Avatar answered Oct 20 '22 00:10

Igor