Reputation: 3151
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.
AuthorizationHandler
, check request identity (parsed by JWT Bearer middleware)httpContext.Items[ClaimTypes.Actor]
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:
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
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