I have a few API endpoints (.net core mvc) that will use the logged-in user (JWT) if the user is logged in, but anyone is allowed to call the method.
By using [AllowAnonymous] together with [Authorize] I get the functionality I'm looking for but if the sent JWT is expired, I'm not getting a 401, but instead it is treated as an anonymous request.
I need the authorization logic to treat the endpoint as it was [Authorize] only if there is a Authorization: Bearer header, meaning that if the token is expired, it should return a 401
This functionality is only needed on a few endpoints and not on a full controller
I've tried the combination of [AllowAnonymous] + [Authorize].
Also tried RequireAssertion when creating the policy, but doesn't seem to have been meant for this
The method I'm using for testing:
[HttpPost]
[Route("testToken")]
[AllowAnonymous]
[Authorize(Policy = AuthFilterConvension.POLICY_AUTHORIZE_WHEN_HAS_BEARER)]
public async Task<IActionResult> testToken()
{
var user = await _signInManager.UserManager.GetUserAsync(HttpContext.User);
return Ok(new {result = user});
}
Setting up auth to support both cookies + JWT:
services.AddAuthorization(o =>
{
o.AddPolicy(AuthFilterConvension.POLICY_AUTHORIZE_WHEN_HAS_BEARER, b =>
{
b.RequireRole("Admin");
b.RequireAuthenticatedUser();
b.AuthenticationSchemes = new List<string> {JwtBearerDefaults.AuthenticationScheme};
});
});
services.AddAuthentication()
.AddCookie()
.AddJwtBearer(cfg =>
{
var issuer = Environment.GetEnvironmentVariable("JWT_ISSUER");
var tokenKey = Environment.GetEnvironmentVariable("JWT_TOKEN_KEY");
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters
{
RequireExpirationTime = true,
RequireSignedTokens = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidIssuer = issuer,
ValidAudience = issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenKey))
};
}
);
I would expect the [Authorize] header to return 401 Unauthorized when using expired tokens instead of calling the method as anonymous. Is this possible to set up?
Update I created the attribute like this:
public class AuthorizeBearerWhenPresent : ActionFilterAttribute, IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var headers = context.HttpContext.Request;
//TODO: Test header
var httpContext = context.HttpContext;
var authService = httpContext.RequestServices.GetRequiredService<IAuthorizationService>();
var authResult = await authService.AuthorizeAsync(httpContext.User, context,
AuthFilterConvension.POLICY_AUTHORIZE_WHEN_HAS_BEARER);
if (!authResult.Succeeded)
{
context.Result = new UnauthorizedResult();
}
}
}
However, no matter what I'm sending to the authService it returns true. Doesn't matter if I send an invalid JWT header or no header. Shouldn't this be the correct way to get executed a policy?