Redplane
Redplane

Reputation: 3151

AuthorizationHandler<T> is not fired in AllowAnonymous controller method

I'm making an application using .Net Core 2.1 and AngularJS. In my .Net Core application, there are some methods that allow both authenticated and unauthenticated users to access.

This is my get personal profile method

    /// <summary>
    ///     Find personal profile.
    /// </summary>
    /// <param name="id">Id of user. 0 for the request sender profile</param>
    /// <returns></returns>
    [HttpGet("personal-profile/{id}")]
    [AllowAnonymous]
    public async Task<IActionResult> FindProfile([FromRoute] int? id)
    {
        // Get requester identity.
        var profile = IdentityService.GetProfile(HttpContext);

        // Search for accounts.
        var accounts = UnitOfWork.Accounts.Search();

        if (id == null || id < 1)
        {
            if (profile != null)
                accounts = accounts.Where(x => x.Id == profile.Id);
            else
                return Ok();
        }
        else
            accounts = accounts.Where(x => x.Id == id);

        // Only search for active account.
        accounts = accounts.Where(x => x.Status == AccountStatus.Available);

        // Find the first account in system.
        var account = await accounts.FirstOrDefaultAsync();
        return Ok(account);
    }

This is my AuthorizationHandler class.

    /// <summary>
    ///     Handle requirement asychronously.
    /// </summary>
    /// <param name="context"></param>
    /// <param name="requirement"></param>
    /// <returns></returns>
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
        SolidAccountRequirement requirement)
    {
        // Convert authorization filter context into authorization filter context.
        var authorizationFilterContext = (AuthorizationFilterContext) context.Resource;

        //var httpContext = authorizationFilterContext.HttpContext;
        var httpContext = _httpContextAccessor.HttpContext;

        // Find claim identity attached to principal.
        var claimIdentity = (ClaimsIdentity) httpContext.User.Identity;

        // Find email from claims list.
        var email =
            claimIdentity.Claims.Where(x => x.Type.Equals(ClaimTypes.Email))
                .Select(x => x.Value)
                .FirstOrDefault();

        // Email is invalid.
        if (string.IsNullOrEmpty(email))
        {
            context.Fail();
            return;
        }

        // Find accounts based on conditions.
        var accounts = _unitOfWork.Accounts.Search();
        accounts = accounts.Where(x =>
            x.Email.Equals(email, StringComparison.InvariantCultureIgnoreCase) && x.Status == AccountStatus.Available);

        // Find the first matched account in the system.
        var account = await accounts.FirstOrDefaultAsync();

        // Account is not found.
        if (account == null)
            return;

        // Initiate claim identity with newer information from database.
        var claimsIdentity = new ClaimsIdentity();
        claimsIdentity.AddClaim(new Claim(ClaimTypes.Email, email));
        claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, account.Nickname));
        claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, Enum.GetName(typeof(AccountRole), account.Role)));
        claimsIdentity.AddClaim(new Claim(ClaimTypes.Authentication,
            Enum.GetName(typeof(AccountStatus), account.Status)));

        // Update claim identity.
        _identityService.SetProfile(httpContext, account);
        context.Succeed(requirement);
    }

This is what I'm doing.

This is what happens in my code: - Use postman to send a request to api/user/personal-profile/0 for getting request sender profile. - In FindProfile, profile variable is null instead of information of user, which means AuthorizationHandler is not triggered.

If I remove [AllowAnonymous] attribute from FindProfile method, everything works fine.

Please see those 2 images I attached below for more information:

With AllowAnonymous

Without AllowAnonymous

My question is: Can we make AuthorizationHandler be triggered with AllowAnonymous method. In my system, there are some APIs can be used by both kind of these users.

Thank you

Upvotes: 1

Views: 2177

Answers (1)

Set
Set

Reputation: 49779

> Can we make AuthorizationHandler be triggered with AllowAnonymous method?

No, you cannot. [AllowAnonymous] bypasses all authorization statements. If you are interested, this is the corresponding code in AuthorizeFilter.OnAuthorizationAsync implementation.

if (context.Filters.Any(item => item is IAllowAnonymousFilter))
{
   return;
}

Think about removing AllowAnonymous and adding some custom logic. For example, you may create own MVC filter. Let's say:

public class MyAllowAnonymous : ActionFilterAttribute {}

Then in AuthorizationHandler checks whether authorizationFilterContext.Filters collection has this filter. If yes, returns context.Succeed(requirement);.

  • consider creating a separate implementation of AuthorizationHandler with another IAuthorizationRequirement.

  • instead of filters check you may directly get the route name by authorizationFilterContext.ActionDescriptor and decide whether anonymous access allowed. But then your AuthorizationHandler should know the list of routes.

Upvotes: 4

Related Questions