Azri Zakaria
Azri Zakaria

Reputation: 1364

Check authorization policy inside the action controller

Because of the API is shared with admin and user. So I want to check the policy inside the action if the user id not equal with the parameter.

To simplify, this application using jwt token as the access token. So as for now have 2 roles which it User and Admin. So I add policy based on claims for this roles. Here how I generate the policy in startup.cs

public static IServiceCollection AddCustomAuthorizationPolicy(this IServiceCollection services) 
{
    services.AddAuthorization(options = >options.AddPolicy("UserOnly", policy = >policy.RequireClaim(CustomClaimTypes.UserManagement, "User", "Admin")));
    services.AddAuthorization(options = >options.AddPolicy("RequireAdmin", policy = >policy.RequireClaim(CustomClaimTypes.UserManagement, "Admin")));

    return services;
}

UserOnly allow both of roles. RequireAdmin for admin only.

Here is the current API I do for change the password

[HttpPost("{id}/change-password")]
[Authorize(Policy = "RequireAdmin")]
public async Task <IActionResult> ChangePassword([FromRoute] string id, [FromBody] UserChangePasswordViewModel model) 
{
    
    //This authorize filter require admin.
    //But I also want allow user to access
    
    var user = await _userManager.FindByIdAsync(id);

    if (user == null) return BadRequest("User not found");
    
    //My business logic here

}

So I change to UserOnly which allow both of roles to access it

[HttpPost("{id}/change-password")]
[Authorize(Policy = "UserOnly")]
public async Task < IActionResult > ChangePassword([FromRoute] string id, [FromBody] UserChangePasswordViewModel model) 
{
    var getCurrentUserId = _identityService.GetUserIdentity();
    
    var user = await _userManager.FindByIdAsync(id);

    if (user == null) return BadRequest("User not found");
    
    if(getCurrentUserId != id)
    {
        //Check if this user is admin
        if(!isAdmin) //<-- Here I want to check the policy
        {
            //Response 403 forbidden
            
        }
    }

    //My business logic here

}

But I not really sure how to check policy inside the action. Any better suggestion? or need to use HttpContext.User for find the claim and check the value?

Upvotes: 4

Views: 4464

Answers (3)

Pablissimo
Pablissimo

Reputation: 2905

You can make a dynamic policy decision by taking a dependency in your controller on IAuthorizationService and calling the IAuthorizationService.AuthorizeAsync method. An example of some fictional controller:

private readonly IAuthorizationService _authorizationService;

public MyController(IAuthorizationService authorizationService) 
{
    _authorizationService = authorizationService;
}

[HttpPost("{id}/protected-action")]
public async Task <IActionResult> SomeDynamicallyProtectedAction() 
{
    var isAdminEvaluationResult =
        await _authorizationService.AuthorizeAsync(User, null, "RequireAdmin");

    if (!isAdminEvaluationResult.Succeeded)
    {
        return Forbid();
    }

    // ...continue processing the request
}

Upvotes: 5

Brando Zhang
Brando Zhang

Reputation: 28352

In my opinion, there is no need to call RequireAdmin policy again in the action method to check the user is admin or not.

You could directly get the CustomClaimTypes.UserManagement claim value from User in the action method. Then you could write some logic to check the user is admin or not.

More details about how to get the CustomClaimTypes.UserManagement claim value and check it ,you could refer to below codes:

    public async Task<IActionResult> Get()
    {
        string role = User.Claims.FirstOrDefault(x => x.Type == CustomClaimTypes.UserManagement).Value;
        if (role != "Admin")
        {
            return Forbid();
        }
        return Ok(new string[] { "value1", "value2" });

    } 

Result:

enter image description here

Upvotes: 2

Łukasz K
Łukasz K

Reputation: 76

Try: User.IsInRole("Admin")

Upvotes: 2

Related Questions