Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web Api Custom Authorize Attribute Properties

I'm trying to extend he default Web Api authorize attribute to allow authenticated users to have access to a set of actions even though they are not registered in the application (e.g., they don't have a role).

 public class AuthorizeVerifiedUsersAttribute : AuthorizeAttribute
    {
        /// <summary>
        /// Gets or sets the authorized roles.
        /// </summary>
        public new string Roles { get { return base.Roles; } set { base.Roles = value; } }

        /// <summary>
        ///  Gets or sets the authorized users.
        /// </summary>
        public new string Users { get { return base.Users; } set { base.Users = value; } }

        private bool _bypassValidation;
        /// <summary>
        /// Gets of sets a controller or an action as an authorization exception
        /// </summary>
        public virtual bool BypassValidation
        {
            get
            {
                Debug.WriteLine("get:" + TypeId.GetHashCode() + " " + _bypassValidation);
                return _bypassValidation;
            }
            set
            {
                Debug.WriteLine("set:" + TypeId.GetHashCode() + " " + value);
                _bypassValidation = value;
            }
        }

        protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                if (BypassValidation)
                {
                    return true;
                }
                else
                {
                   //return false if user is unverified

                }
            }

            return base.IsAuthorized(actionContext);
        }
    }

And it is being used like this:

 [AuthorizeVerifiedUsers]
 public class UserProfileController : ApiController
 {

    [AuthorizeVerifiedUsers(BypassValidation = true)]
    public bool Verify(string verificationCode)
    {}
 }

So far this action is the only that is using the BypassValidation = true.

The issue arises because the BypassValidation property is false for the action even though the Debug window - used in the BypassValidation property - shows the following:

set:26833123 True set:39602703 True get:43424763 False get:43424763 False get:43424763 False //call that should have "True"...

I noticed two things:

  • The TypeId (The unique identifier for the attribute) is different between the calls that have BypassValidation = true and the ones that have BypassValidation = false.
  • The id '43424763' doesn't have a corresponding set

Any ideas?

Thanks in advance, Joao

like image 871
JCS Avatar asked Jan 02 '14 14:01

JCS


2 Answers

The way Web API works is that the authorize attribute is called for the parent scope, in this case the controller, and the override (authorize attribute on the action) needs to be done manually (Please correct me if I'm wrong).

Therefore a solution could look like the following:

public class AuthorizeVerifiedUsersAttribute : AuthorizeAttribute
{
  (...)

  protected override bool IsAuthorized(HttpActionContext actionContext)
  {

     if (HttpContext.Current.User.Identity.IsAuthenticated)
     {
        //retrieve controller action's authorization attributes
        var authorizeAttributes = actionContext.ActionDescriptor.GetCustomAttributes<AuthorizeVerifiedUsersAttribute>();

        //check controller and action BypassValidation value
        if (BypassValidation || 
            actionAttributes.Count > 0 && actionAttributes.Any(x => x.BypassValidation))
        {
            return true;
        }
        else
        {
          //return false if user is unverified
        }

        return base.IsAuthorized(actionContext);
    }
 }
like image 140
JCS Avatar answered Oct 07 '22 15:10

JCS


A bit too late, but for other users with similar problems: in Web API 2 you can override all previous authorization attributes (global authorization filters, controller authorization attributes, etc.) using "OverrideAuthorization" and afterwards just use the Authorize attribute, without specifying the role. The default behavior of the Authorize attribute is just to check if the user is authenticated.

In this case:

[YourCustomAuthorize]
public class UserProfileController : ApiController
{
    [OverrideAuthorization]
    [Authorize]
    public bool Verify(string verificationCode)
    {
    // TODO
    }
}
like image 33
bojank Avatar answered Oct 07 '22 13:10

bojank