Naurto san
Naurto san

Reputation: 417

Role Based Authentication for web API not working

I'm facing an issue while working with Role-Based authentication for web APi.

I have a controller class where the controller has a custom authorize attribute called Myauthorize. I have a method inside the controller which can be accessed only with Admin access. But the same method has been calling with QA access as well. Could anyone please help with the below?

Please find the code below. Controller :

namespace Hosiptal.Controllers.office
{
 [MyAuthorize(Constants.Roles.Admin)]
 public class UserRolesController : ApiController
 {
    private readonly IRepository<EntityModels.Role> rolesRepository;

    public UserRolesController(IRepository<EntityModels.Role> rolesRepository)
    {            
        this.rolesRepository = rolesRepository;
    }

    // GET: Users
    [HttpGet]
    [Route("")]        
    public IEnumerable<Role> GetAll()
    {
        return this.rolesRepository.GetAll()
            .ToArray()
            .Select(r => Mapper.Current.Get<Role>(r));                
    }
  }
}

MyAuthorize has followed.

namespace Hospital.Web.Filters.WebApi
{

 public class MyAuthorize: AuthorizeAttribute
 {
    private readonly string[] allowedroles;
    private static IUserProfileRepository UserProfileRepository
    {
        get { return IoC.Current.Resolve<IUserProfileRepository>(); }
    }
    public MyAuthorize(params string[] roles)
    {
        this.allowedroles = roles;
    }

    public override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken 
      cancellationToken)
    {
        var claimsIdentity = actionContext.RequestContext.Principal.Identity as ClaimsIdentity;
        
        var alias = claimsIdentity.Name.Split('@')[0];
        if (alias == null)
        {
            throw new ArgumentNullException(nameof(actionContext));
        }
        user(alias);
        return base.OnAuthorizationAsync(actionContext, cancellationToken);
    }

    public static GenericPrincipal user(string userName)
    {
        userName = userName.ToUpper();

        var userProfile = UserProfileRepository.Get(userName) ?? new UserProfile()
        {
            UserName = userName,
            Roles = new List<Role>(),
            FirstLoginDateUtc = DateTime.UtcNow
        };
        return CreatePrincipal(userProfile);
    }
    public static GenericPrincipal CreatePrincipal(UserProfile user)
    {
        var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, 
                           user.UserName) }, "Custom");
        return new GenericPrincipal(identity, user.Roles.Select(i => 
              i.Name).ToArray());
    }
   }
 }

How can restrict the user here based on access level?

Upvotes: 1

Views: 2687

Answers (3)

JuanR
JuanR

Reputation: 7783

If you review the source code for the AuthorizeAttribute class, you will see that it uses the controller context request's principal to perform authorization, so override the IsAuthorized method instead, move your code there and assign the principal you create to the context request's principal:

protected override bool IsAuthorized(HttpActionContext actionContext)
{
    var claimsIdentity = actionContext.RequestContext.Principal.Identity as ClaimsIdentity;

    var alias = claimsIdentity.Name.Split('@')[0];
    if (alias == null)
    {
        throw new ArgumentNullException(nameof(actionContext));
    }
    
    //This sets the context's principal so the base class code can validate
    actionContext.ControllerContext.RequestContext.Principal = user(alias);
    
    //Call the base class and let it work its magic
    return base.IsAuthorized(actionContext);
}

I will refrain from commenting on the design itself. This should fix your issue.

Upvotes: 1

Jakub Kozera
Jakub Kozera

Reputation: 3473

You don't need to create your own authorize filter for this. Use the built-in [Authorize(Roles = "Admin")] - which will check if the user has a claim called "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" and if that value matches to the one you put in that authorize attribute, the authorization will succeed.

So in your case just make sure, when you log in the user to set his claim with the role like this:

            var claims = new List<Claim>()
            {
                new Claim(ClaimTypes.Role, "Admin"), //here set the users role
                     // ... other claims
            };

(ClaimTypes class is from the namespace System.Security.Claims)

And then the [Authorize(Roles = "Admin")] should work just fine

Upvotes: 0

Wowo Ot
Wowo Ot

Reputation: 1529

This is what's working for me

public class AdminAuthorizeAttributee : AuthorizeAttribute
{

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        if (AuthorizeRequest(actionContext))
        {
            return;
        }
        HandleUnauthorizedRequest(actionContext);
    }

    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        base.HandleUnauthorizedRequest(actionContext);
    }

    private bool AuthorizeRequest(HttpActionContext actionContext)
    {
        try
        {
            var username = HttpContext.Current.User.Identity.Name;
            var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
            var user = userManager.Users.Where(a => a.UserName == username).FirstOrDefault();
            var rolesForUser = userManager.GetRoles(user.Id);
            var role = "Admin";



            if (rolesForUser.Contains(role))
            {
                return true;
            }
            return false;
        }
        catch (Exception ex)
        {
            return false;
        }

    }

}

and in Controller

[AdminAuthorizeAttributee]
public class YOUR_Controller : ApiController

Upvotes: 0

Related Questions