Let me give you some context: I am trying to implement custom policies in an ASP.NET Core Web API while using JWT bearer tokens.
But for some reason when trying to reach the protected routes, this error shows up:
05:02:07 INF] Authorization failed. These requirements were not met: RolesAuthorizationRequirement:User.IsInRole must be true for one of the following roles: (Sudo)
05:02:07 INF] AuthenticationScheme: Identity.Application was challenged. 05:02:07 INF] Request finished
HTTP/1.1 GET http://localhost:5259/api/auth/ - 302 0 null 6.7246ms
05:02:07 INF] Request starting HTTP/1.1 GET http://localhost:5259/Account/Login?ReturnUrl=%2Fapi%2Fauth%2F - application/json null
I understand this basically says that the user couldn't be authorized successfully.
I've checked all the relevant configuration and I can't seem to understand why it isn't working. I was even thinking maybe it was Insomnia which was not properly giving the bearer token but I've tested other projects and they do work in said matter.
I am working on a refresh/access token based authorization. To my understanding the default Token for Authorization its the bearer token within the headers. Which gets sends when you add it to Insomnia. The string itself.
I also have the refresh token as an httponly token. But I haven't tested it yet as I am still figuring out the register and login endpoints.
I've yet found why this isn't working. Let me give you some relevant code for you to understand better my setup.
This is my relevant Program.cs configuration:
public static void ConfigureAuthentication(this IServiceCollection services, IConfiguration config)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = config["JWT:Issuer"],
ValidateAudience = false,
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["JWT:Key"] ?? throw new Exception("Security Key not found within configuration")))
};
});
}
public static void ConfigureAuthorization(this IServiceCollection services, IConfiguration config)
{
services.AddAuthorization(options =>
{
options.AddPolicy("SudoPolicy", p => p.AddRequirements(
new IsSudoRequirement()
));
options.AddPolicy("SudoRole", p => p.RequireRole("Sudo"));
});
}
public static void ConfigureIdentity(this IServiceCollection services)
{
services.AddIdentity<Usuario, IdentityRole>(options =>
{
options.Password.RequiredLength = 6;
}).AddEntityFrameworkStores<AppDbContext>();
}
This is my custom policy. It wasn't meant to be used but I figured I'd try implementing one and just seeing if I could make it work that way.
public class IsSudoHandler : AuthorizationHandler<IsSudoRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsSudoRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "Role" && c.Value.Contains("Sudo")))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class IsSudoRequirement : IAuthorizationRequirement
{
public IsSudoRequirement()
{
IsSudo = true;
}
public bool IsSudo { get; }
}
Now the way I am handling the return of the access token is as such:
public async Task<string> CreateAccessTokenAsync(Usuario usuario)
{
var roles = await _userManager.GetRolesAsync(usuario);
var claims = new List<Claim>()
{
new Claim(JwtRegisteredClaimNames.Sub, usuario.UserName!),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString())
};
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
var creds = new SigningCredentials(_key, SecurityAlgorithms.HmacSha256);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.UtcNow.AddMinutes(10),
NotBefore = DateTime.UtcNow.AddSeconds(5),
Issuer = _config["JWT:Issuer"],
SigningCredentials = creds
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
This is how I am testing this specific token:
And this is how I try to test it:
[HttpGet]
[Authorize("SudoRole")]
public IActionResult Test()
{
return Ok("Poto");
}
And it throws that error. I've trying doing different policies. Using roles. Using claims. But I've yet found why it isn't working. Maybe I am doing something wrong not doing something. I've yet found why it is giving me said error.
This is the full repo in case you are curious: Github Repository
Any guidance, resource or advice with how to fix this issue as well as with the architecture and the way the program is structured would be highly appreciated.
Thank you for your time!