Why should I define JwtBearerDefaults.AuthenticationScheme for my Authorize attribute always?

Viewed 3912

I have a simple sample for JWT Authentication which you can find it here

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>

As you can see I have added JwtBearerDefaults.AuthenticationScheme to the Authentication inside Startup/ConfigureServices method so I should be able to use the [Authorize] standalone as following

[Authorize]
public sealed class WeatherForecastController : BaseController
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

but I dont know why it does not work! (I have tested with Postman). I must define it via AuthenticationSchemes.

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public sealed class WeatherForecastController : BaseController
{
    private static readonly string[] Summaries = new[]
    {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

Can any one guide me how can I use Authorize attribute standalone without AuthenticationSchemes especially when I have defined it inside ConfigureServices? What did I set wrong?

3 Answers

Your code does not have app.UseAuthentication() included. So that means there is no authentication middleware.

The default authentication scheme is used only by the authentication middleware to auto-authenticate the request and build-up the current principal. When you use [Authorize] alone, the authentication schemes are not set and the authorization filters will not work. The filter built from that IAuthorizeData (AuthorizeAttribute is an IAuthorizeData) is an AuthorizeFilter and this requires the schemes to be set explicitly.

That's why the other [Authorize(...)] with some scheme set works but not the [Authorize].

So you should use the authentication middleware to use [Authorize] conveniently without having to specify the schemes.

The AuthorizeFilter will use IPolicyEvaluator internally to authenticate the request, here's the default implementation of PolicyEvaluator.AuthenticateAsync - you can see it checks for the availability of AuthenticationSchemes.

In .NET 5, you can config as below:

Add JwtBearerDefaults.AuthenticationScheme as default scheme string in AddJwtBearer

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
      .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwt =>
       {
         jwt.TokenValidationParameters = new TokenValidationParameters
           {
             ValidateIssuer = true,
                       ....
            ;
                
         });

Use in Configure (in case using Role as well)

  app.UseAuthentication();
  app.UseAuthorization();

And then you can use [Authorired] Attribute as default

[ApiController]
[Route("api/data")]
[Authorize]
public class DataController : ControllerBase

There is also a conflict between AddIdentityCore() and AddIdentity() methods. If you use AddIdentity, it uses Cookie based authentication so JWT doesnt work if you dont write [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] explicitly. I changed my startup as below and now it works with [Authorize] in .Net 6. I am using custom classes for both user and role.

services.AddIdentityCore<AppUser>(x =>
{
    x.Password.RequireDigit = false;
    x.Password.RequiredLength = 4;
    x.Password.RequireLowercase = false;
    x.Password.RequireNonAlphanumeric = false;
    x.Password.RequireUppercase = false;
})
.AddRoles<AppRole>()
.AddEntityFrameworkStores<DBContext>();
Related