'.NET Authorization Handler running before middleware
I am trying to add in authorization middleware which sets the user in a dependency injected interface. I have tested this and is successful.
I then wanted to add a authorization handler which checks the users role vs the role which is expected, for example to limit certian actions of the api to superusers.
I've created the authorization handler which is here, as you can see I'm dependency injecting the IUserProvider, the authenticated user is set within the middleware of this method.
public class RoleHandler : AuthorizationHandler<RoleRequirement>, IAuthorizationHandler
{
private readonly IUserProvider _userProvider;
public RoleHandler(IUserProvider userProvider)
{
_userProvider = userProvider;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
{
var currentUser = _userProvider.GetAuthenticatedUser();
if (requirement.Roles.Contains(currentUser.RoleId))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
AuthenticationMiddleware:
public async Task InvokeAsync(HttpContext context, IUserRepository userRepository, IUserProvider userProvider, ApplicationContext applicationContext)
{
var email = context.User.Claims.FirstOrDefault(claim => claim.Type == "preferred_username");
if (email == null)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
var user = await userRepository.FindByEmailAsync(email.Value);
if (user == null)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return;
}
userProvider.SetAuthenticatedUser(user);
await _next.Invoke(context);
}
I can see that the AuthorizationHandler is getting called before the AuthorizationMiddleware which is why this is occuring.
However, I've tried to check the context within the HandleRequirementAsync and the user in here is also null.
Here is my user provider, as you can see very basic:
public class UserProvider : IUserProvider
{
private static User AuthenticatedUser { get; set; } = default!;
public User GetAuthenticatedUser()
{
return AuthenticatedUser;
}
public void SetAuthenticatedUser(User user)
{
AuthenticatedUser = user;
}
}
Is there anything I can do to alter the execution order?
EDIT:
Forgot to add in the controller where I am using this:
[Authorize(Policy = "Superuser")]
[Route("{id}"]
public async Task<User> UpdateAsync([FromBody] User user, int id)
{
return await _userService.UpdateAsync(user);
}
And the Program.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Identity.Web;
using System.IdentityModel.Tokens.Jwt;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDataAccessLayer();
builder.Services.AddHttpContextAccessor();
builder.Services.AddDbContext<ApplicationContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("Database")).EnableSensitiveDataLogging().EnableDetailedErrors();
});
builder.Services.AddScoped<IAuthorizationHandler, RoleHandler>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Superuser", policy => policy.Requirements.Add(new RoleRequirement(Role.SUPER_USER)));
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<AuthenticationMiddleware>();
app.MapControllers();
app.Run();
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
