1

I'm trying to implement a controller with authorization. Adding the [Authorize] attribute results in a 404 response. However, I was able to get it working by using [Authorize(AuthenticationSchemes = "Bearer")] instead (as explained in this StackOverflow post).

The issue is that I cannot set the "Bearer" authentication scheme as the default for all controllers. I tried the following solutions but couldn't get them to work:

  1. Solution 1
  2. Solution 2

The second solution works, but the problem is that adding [Authorize(Roles = "Admin")] to an endpoint results in a 404 response.

var builder = WebApplication.CreateBuilder(args);

var jwtSection = builder.Configuration.GetSection("JwtSection");
var theKey= Encoding.UTF8.GetBytes(jwtSection["TheKey"] ?? string.Empty);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = jwtSection["Issuer"],
            ValidAudience = jwtSection["Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(theKey),
            RoleClaimType = ClaimTypes.Role,
        };
    });

builder.Services.AddAuthorization();

builder.Services.AddHttpContextAccessor();

builder.Services.AddDbContext<MyDbContext>(options =>
    options.UseMySql(builder.Configuration.GetConnectionString("Connection"),
        ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("Connection"))));

builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<MyDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddScoped<IRequestContextService, RequestContextService>();
builder.Services.AddTransient<IAuthService, AuthService>();
builder.Services.AddTransient<IUsersService, UsersService>();
builder.Services.AddTransient<ITokenService, TokenService>();

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Initialize roles (if they don't exist)
using (var scope = app.Services.CreateScope())
{
    var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    await RoleInitializer.InitializeRoles(roleManager);
}

app.UseRouting();

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

app.UseMiddleware<ExceptionHandlingMiddleware>();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapControllers();

app.Run();


2
  • Hi,@DonAlex1897 could you share your codes in program.cs, so that we could reproduce your issue? Commented Feb 10 at 9:55
  • Hi @RuikaiFeng, Just added the Program.cs code. Hope it helps. Commented Feb 10 at 11:41

1 Answer 1

2

You got 404 error for the codes here which configure the default scheme as IdentityConstants.ApplicationScheme( Identity.Application ) :

builder.Services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<MyDbContext>()
    .AddDefaultTokenProviders();

It was based on cookie authentication and if you fail authentication it would redirect to /Account/Login and result in 404 error

public static OptionsBuilder<CookieAuthenticationOptions> AddApplicationCookie(this AuthenticationBuilder builder)
 {
     builder.AddCookie(IdentityConstants.ApplicationScheme, o =>
     {
         o.LoginPath = new PathString("/Account/Login");
         o.Events = new CookieAuthenticationEvents
         {
             OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
         };
     });
     return new OptionsBuilder<CookieAuthenticationOptions>(builder.Services, IdentityConstants.ApplicationScheme);
 }

The issue is that I cannot set the "Bearer" authentication scheme as the default for all controllers.

Two solutions for you:

1,register authetication services in correct order as below:

builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<MyDbContext>()
.AddDefaultTokenProviders();
    ......
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            .....
        });

builder.Services.AddAuthorization();

Also set the default authentication scheme as below:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
        {
            .....
        });

2,append authorize data to your controller endpoints as below:

app.MapControllers().RequireAuthorization(new AuthorizeAttribute() { AuthenticationSchemes= JwtBearerDefaults.AuthenticationScheme,Roles="",Policy="" });
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks @RuikaiFeng, both solutions worked for me. Just for the first solution to work, I had to change the default authentication as: builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; })

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.