Custom Authorize for both action and api calls? - asp.net-mvc

I recently asked this question and have successfully setup my custom authorize attribute.
But now I have hit another wall regarding api calls. I need these calls to also be authorized in the same fashion as the action calls. I understand that there is a difference between System.Web.Http and System.Web.Mvc Authorize attributes. So I have created a separate Api specific attribute that basically does the same thing. However, I am having trouble setting the User - principal and identity like i do in the original attribute.
My attributes simply check for some values in a cookie to authorize the request, once the attribute has read the cookie I was storing the decrypted cookie information within a custom principal/identity setup. In my Api call, when I go to retrieve this information from the identity my cast fails and i receive a null value.
This is how I store the information
Api
HttpContext.Current.User = new MyPrinciple(new MyIdentity(decCookie.Name, decCookie.UserData));
Action
filterContext.RequestContext.HttpContext.User = new MyPrinciple(new MyIdentity(decCookie.Name, decCookie.UserData));
How I retrieve the desired information I assumed would be the same
(User.principal.Identity as MyIdentity).MyData;
Questions
Do I really need to have 2 separate attributes
For the Api attribute how can I easily store the information for later use within the controller. Or basically can I not actually get/set the Identity this way for these calls?
EDIT #1
I found how to properly access my cookie value from my ApiController, I was simply missing a reference to System.Web >_<. So question #2 has been solved! but #1 still remains.

Web API and MVC have nothing in common (technically) - even when they look the same. You need two separate attributes.

You can only inherit in one class from in c#, and each authorizeattribute lives in its own namespace, so you can't do it in a single class.
You could hold it in a common namespace and then call a common class to do the lifting.
Possible solution (untested)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace Common.Attributes
{
public class CustomAuthorize : System.Web.Mvc.AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (!HttpContext.Current.Request.IsAuthenticated || (HttpContext.Current.User as User) != null)
return;
filterContext.HttpContext.User = Authorize.ExtractIdentity(filterContext.HttpContext);
}
}
public class CustomHttpAuthorize : System.Web.Http.AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (!HttpContext.Current.Request.IsAuthenticated || (HttpContext.Current.User as User) != null)
return;
System.Threading.Thread.CurrentPrincipal = Authorize.ExtractIdentity(filterContext.HttpContext);
}
}
}
public static class Authorize
{
public static IIdentity ExtractIdentity(HttpContext context)
{
// do your magic here
}
}
}

Related

Custom Authorization in Asp.net WebApi call only for authorize attribute

I have implemented token based authentication.
I want to write custom authorize attribute.
The reason behind this is, sometime UserIdentity.GetUserId() gives null.
So to handle this I have written the custom authorize attribute, like the following.
This custom Authorize calls for all the calls (anonymous or authorize call).
So I have used the property IsAuthorizeCall to check whether the call is coming from anonymous or authorized.
Can I make the calls like, I want to call this custom authorzie only when I mentioned above method. Otherwise this should not be called.
How can I do that?
public class CustomAuthorize : AuthorizationFilterAttribute
{
public bool IsAuthorizeCall { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (IsAuthorizeCall)
{
IdentityHelper IdentityHelper = new IdentityHelper();
if (IdentityHelper.UserId== Guid.Empty)
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
}
Referenced:
Custom Authorization in Asp.net WebApi - what a mess?
How to Customize ASP.NET Web API AuthorizeAttribute for Unusual Requirements
I have found the answer myself.
If add the filter to the global asax, then it will be called for all
the controllers and action even if mentioned or not.
So we need to remove the following from global.asax
GlobalConfiguration.Configuration.Filters.Add(new CustomAuthorize());
So now, the custom athorize will be call only when mentioned in the controller or action.

Add roles to ADFS IPrincipal

I have been looking for answer to this question for a few days now, but I have not found any success. I would post the links, but it would probably take up the entire page.
So here is what I have...
I have an MVC application, which uses the WC-Federation protocol. I have been able to configure the application, so that it authenticates the users, and returns the claims from ADFS. This works perfect. I can also extract all the claims with no issues. But I am doing this within one of the actions in the controller.
And here is what I want to do...
I want to use ADFS to authenticate the user, but I want to use my own internal roles to authorize the user to have access to specific controllers (e.g. [Authorize(Roles = "CoolRole")]). I want to be able to do this, because I already have a Web API that uses OAuth 2.0, with a backend SQL Server database to manage users and roles (internal and external user.) I now want a secure portal that will allow internal users to access the data with a single-sign-on experience. Looking at the Controller model, I noticed there are some properties associated with the authentication process (OnAuthentication, OnAuthenticationChallenge) and one for the authorization process (OnAuthorization.)
I don't necessarily need the code, but I feel like I've hit a brick all, and I need to be pointed in the right direction.
UPDATE
I tried this:
protected override void OnAuthorization(
System.Web.Mvc.AuthorizationContext filterContext)
{
//Private class to create a new IPrincipal based on my AppUserMgr
var user = _setCurrentUser(
(ClaimsIdentity)filterContext.HttpContext.User.Identity);
filterContext.HttpContext.User = user;
base.OnAuthorization(filterContext);
}
This returned a 401 (Unauthorized) response.
and...
protected override void OnAuthentication(
System.Web.Mvc.Filters.AuthenticationContext filterContext)
{
//Private class to create a new IPrincipal based on my AppUserMgr
var user = _setCurrentUser(
(ClaimsIdentity)filterContext.HttpContext.User.Identity);
filterContext.Principal = user;
base.OnAuthorization(filterContext);
}
This just calls the STS numerous times, before it fails. I even tried swapping after the assignment to after the base is called in both. No luck.
Prior to the previous ones, I also tried to add an AuthorizeFilter to the control, but that didn't help:
http://pratapreddypilaka.blogspot.in/2012/03/custom-filters-in-mvc-authorization.html
I found this link: http://brockallen.com/2013/01/17/adding-custom-roles-to-windows-roles-in-asp-net-using-claims/
From there, I guessed my way through
Here is the basics of what I did:
I ended up overriding the OnAuthentication method of the Controller, but still made sure to call the base. I did this from within an extended class. Here is the concept:
public class AdfsController : Controller
{
//Some code for adding the AppUserManager (used Unity)
protected override void OnAuthentication(
System.Web.Mvc.Filters.AuthenticationContext filterContext)
{
base.OnAuthentication(filterContext);
//Private method to set the Principal
_setCurrentUser(filterContext.Principal);
}
private void _setCurrentUser(IPrincipal principal)
{
//Put code to find to use your ApplicationUserManager or
//dbContext. roles is a string array
foreach(var role in roles)
{
((ClaimsIdentity)((ClaimsPrincipal)principal).Identity)
.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
}
In the Controller, you can now add the follow:
public class HomeController : AdfsController
{
//I used a magic string for demo, but store these in my globals class
[Authorize(Roles = "CoolRole")]
public ActionResult Index()
{
return View();
}
}
I tested this by checking a role assigned to the current user, and that worked! Then I changed the role to something like "reject", which the user was not assigned; and I received a 401 Unauthorized.
ADFS is the authentication/token service in Azure. to enable the Roles Based Authentication, you can use Azure RBAC (Role Based Access Controll) service to basically Augment the claims that you get back from the ADFS and add the roles that you get back from RBAC to the token, and use the same token in your API so lock down or secure the backend with that augmented token...
here is the reference for RBAC:
http://azure.microsoft.com/en-in/documentation/articles/role-based-access-control-configure/

Custom Attribute Not Firing

I have a project with both MVC and Web API controllers in it. It uses Windows Auth. However I want to protect a particular Web API action with a custom attribute. I have created the following just to get the basic flow set up (ultimately it will check the users IP address):
using System.Web;
using System.Web.Http;
using System.Web.Http.Filters;
namespace UoB.People.UserInterface.Mvc.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class IPAuthorize : AuthorizationFilterAttribute
{
protected bool AuthorizeCore(HttpContextBase httpContext)
{
return true;
}
}
}
I have decorated my Web API action with a call to this attribute. Problem is, when I run my code locally, the attribute code is never hit. The action is. This particular action belongs to a controller which does not use Windows Auth.
Why is my attribute not being called? Does it need to be registered somewhere? Is there a conflict because my project contains both MVC and Web API controllers? Have I made a simple error somewhere?
Thanks.
You need to register your attribute in RegisterGlobalFilters in your FilterConfig.cs class.
Hope this helps.
Finally figured out my problem. I actually needed to override OnAuthorization and AuthorizeCore. The following example demonstrates this:
public class IPAuthorize : AuthorizeAttribute
{
public string AuthorisedIPs { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
if (AuthorizeCore((HttpContextBase)actionContext.Request.Properties["MS_HttpContext"]))
return;
base.HandleUnauthorizedRequest(actionContext);
}
protected bool AuthorizeCore(HttpContextBase httpContext)
{
// Logic here.
}
}
Hope this helps if anyone else is having trouble.

Good approach for globally accessing the Logged-in User

I want to be able to globally access the logged-in user in (Controllers, HtmlHelpers and other helper classes) throughout the whole application without getting exceptions that User is null and with the ability to handle guests (not-logged-in users).
Should this be handled within my UsersRepository? If so, how to? or is there a better approach?
You can create custom identity and principal classes. Your custom identity class can extend whatever you are currently using and can then simply add the extra information you need.
Then in a global action filter, simply overwrite the current principal with your customized one. Then you can access it from anywhere like a normal identity, but if you need your additional information, you simply cast it to your custom identity class. Which will grant you access to your additional information.
You can write a custom action filter that is executed on every request (you register it as a global filter). This filter would load the user (from the user´s repository for example) and put it the http context for example or in the ViewData.
EDIT:
Ok, the code for the filter could look like this (in this case, it loads the user to the ViewData collection). I didn´t consider anonymous users here.
public class LoadUserToViewDataAttribute : ActionFilterAttribute
{
private IUserRepository _userRepository;
public LoadUserToViewDataAttribute(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller;
var userName = filterContext.HttpContext.User.Identity.Name;
var user = _repository.GetUser(userName);
controller.ViewData.Add("CurrentUser", user);
}
}

Accessing custom principal within a custom ActionFilterAttribute

I am working on an ASP.NET MVC application. I have implemented custom membership provider, principal and identity. In the custom provider I replace the HttpContext.Current.User in the ValidateUser() method as follows:
public sealed class CustomMembershipProvider : MembershipProvider {
...
public override bool ValidateUser(string username, string password) {
...
CustomIdentity identity = new CustomIdentity(...);
CustomPrincipal cu = new CustomPrincipal(identity);
HttpContext.Current.User = cu;
...
}
...
}
In the AccountController (calls the custom membership provider) I am able to access the custom identity as follows:
public class AccountController : BaseController {
...
public ActionResult LogOn(string userName,
string password,
bool rememberMe,
string returnUrl) {
...
CustomIdentity ci = (CustomIdentity)HttpContext.User.Identity;
...
}
...
}
All my controllers inherit the BaseController which calls a custom attribute as follows:
[CustomAttribute]
public abstract class BaseController : Controller {
...
}
I want my other controllers to access the custom identity within the custom attribute after it has been set by the AccountController as follows:
public class CustomAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
base.OnActionExecuting(filterContext);
CustomIdentity ci = filterContext.HttpContext.User.Identity as CustomIdentity;
...
}
}
}
What I have found is that filterContext.HttpContext.User is still set to GenericPrincipal and not my CustomPrincipal. So my custom identity is not accessible within my attribute filter. What do I have to do so that my CustomPrincipal is accessible within my attribute filter?
Thanks in advance.
After researching more about how application request events are fired (the specific order) and when context objects can be set I was able to set my custom principal and identity so that they are available to the filter (throughout the application for that matter).
I realized that the user must be authenticated before these entities could be set for use through the rest of the application. This, I found, could be done in the Application_AuthenticateRequest() method of global.asax.
So, I modified my logic as follows:
Removed creating custom principal and identity from the custom provider's ValidateUser() method.
Instead, ValidateUser() after verifying username and password against the custom repository, caches whatever information I needed within HttpContext.Current.Cache using the user name as the unique key.
Finally, I added the following logic in Application_AuthenticateRequest() to set set my custom principal and identity by extracting the generic identity properties and extending it with custom properties that I stored in the cache. I indexed into the cache using the name stored within generic identity as that is the key I used to create the cache.
protected void Application_AuthenticateRequest(object sender, EventArgs e) {
if (Request.IsAuthenticated) {
// TODO: Add checks so we only do the following once per login.
// Get the GenericPrincipal identity
IIdentity ui = HttpContext.Current.User.Identity;
/* Extract Name, isAuthenticated, AuthenticationType from
the identity of the GenericPrincipal and add them including
any custom properties to the custom identity. I added a
few extra properties to my custom identity. */
CustomIdentity identity = new CustomIdentity(...);
/* Although my custom principal does not currently
have any additional properties, I created a new
principal as I plan to add them in the future. */
CustomPrincipal principal = new CustomPrincipal(identity);
// Set custom principal
HttpContext.Current.User = principal;
}
}
This got me past my hurdle. Please guide me if there are other better ways to accomplish the same.
Thanks.
I don't know if this is "better" way, but it worked for me so far. I create a static UserContext class that has CurrentUser property. There I store the user entity I get from database and use it for user info data and authorization. I only use the HttpContext.Current.User to check authentication.
Now the CurrentUser property stores my user object in HttpContext's Items colletion (I have a wrapper around that so I can make it unit testable).

Resources