I have JWT-based claims authentication/ authorization set up in my .NET Core application, which authenticates as expected, but my policy enforcement is not acting as I would expect.
I have a requirements implementation and handler set up as follows:
public class ImpersonationRequirement : IAuthorizationRequirement
{
}
public class ImpersonationHandler : AuthorizationHandler<ImpersonationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
ImpersonationRequirement requirement)
{
if (context.User.CanImpersonate()) context.Succeed(requirement);
return Task.CompletedTask;
}
}
I have a helper set up like so:
public static bool CanImpersonate(
this ClaimsPrincipal principal)
{
var val = principal?.FindFirst(MyClaimTypes.CAN_IMPERSONATE)?.Value;
return bool.TryParse(val, out var value) && value;
}
public class MyClaimTypes
{
/// <summary>
/// Boolean value indicating this user is authorized to impersonate other customer accounts.
/// </summary>
public const string CAN_IMPERSONATE = "cim";
...
/// <summary>
/// Actual name of the user impersonating the current user.
/// </summary>
public const string IMPERSONATING_USER = "imp";
}
...off of my Startup.cs
, I have the policy defined:
services.AddAuthorization(options =>
{
options.AddPolicy("Impersonator", policy => policy.Requirements.Add(new ImpersonationRequirement()));
});
...and on my controller, it's written as such:
[Produces("application/json")]
[Authorize(Policy = "Impersonator")]
public class ImpersonationController : Controller
{
private readonly ILogger _logger;
private readonly ITokenManagementService _tokenManagementService;
private readonly UserManager<MyUser> _userManager;
public ImpersonationController(ITokenManagementService tokenManagementService, ILoggerFactory loggerFactory, UserManager<MyUser> userManager)
{
_tokenManagementService = tokenManagementService;
_userManager = userManager;
_logger = loggerFactory.CreateLogger<ImpersonationController>();
}
[HttpPost]
[Route("~/api/impersonation/token")]
[ProducesResponseType(typeof(AuthenticationResponse), 200)]
[ProducesResponseType(typeof(Exception), 500)]
public async Task<IActionResult> Impersonate([FromBody] string userNameToImpersonate)
{
try
{
var impersonated = await _userManager.FindByNameAsync(userNameToImpersonate);
if (impersonated == null) throw new EntityNotFoundException($"Unable to find user '{userNameToImpersonate}' in the data store.");
var actualUserId = User.UserId();
var token = await _tokenManagementService.GenerateJwt(impersonated.Id, actualUserId);
var refresh = await _tokenManagementService.GenerateRefreshToken(impersonated.Id, actualUserId);
var response = new AuthenticationResponse {AuthenticationToken = token, RefreshToken = refresh};
return Ok(response);
}
catch (Exception ex)
{
return new OopsResult(ex);
}
}
}
If I run this with the AuthorizeAttribute
commented out, I can take a look at the user's claims, and the "cim: true" is in the claims enumeration, but if I run it with the AuthorizeAttribute
enabled, I get a 403 Forbidden error.
I tried putting a breakpoint on the line in the ImpersonationHandler
:
if (context.User.CanImpersonate()) context.Succeed(requirement);
...but the debugger never stops here, so I don't know what the problem is. Can someone educate me as to what I'm doing wrong?
AddAuthorizationCore(IServiceCollection)Adds authorization services to the specified IServiceCollection. C# Copy. public static Microsoft.Extensions.DependencyInjection.
IAuthorizationRequirement is a marker service with no methods, and the mechanism for tracking whether authorization is successful.
In ASP.NET Core, authentication is handled by the authentication service, IAuthenticationService, which is used by authentication middleware. The authentication service uses registered authentication handlers to complete authentication-related actions.
It seems you forgot to register your ImpersonationHandler
in DI container (which is indeed easy to forget):
services.AddSingleton<IAuthorizationHandler, ImpersonationHandler>();
Asp.net resolves all such handlers from container and tries to match for specific requirement. Since no such handler is registered - nothing sets context.Succeed
and whole authorization fails.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With