Search code examples
.netasp.net-coreasp.net-web-api.net-8.0

I get a 404 response when adding [Authorize] to the controller in a .NET 8.0 Web API


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();



Solution

  • 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();
    

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

    app.MapControllers().RequireAuthorization(new AuthorizeAttribute() { AuthenticationSchemes= JwtBearerDefaults.AuthenticationScheme,Roles="",Policy="" });