After adding Jwt authentication to the Web API, it seems okay if I do not specify roles in the Weather Forecast Controller method. After I specified Roles, only JWT authorisation works. I tried various things, transforming claims and others, but nothing helped. When .net identity authentication is used, it results in 401 Unauthorized.
[Full Source code][https://drive.google.com/file/d/1N57j7NYkH-d_pS3cYAEU_yTVZF47JqcV/view?usp=drive_link]
Startup Class
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
// Add services to the container.
// For Entity Framework
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(configuration.GetConnectionString("ConnStr")));
// For Identity
builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddClaimsPrincipalFactory<AppClaimsPrincipalFactory>()
.AddDefaultTokenProviders();
// Adding Authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
// Adding Jwt Bearer
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = configuration["JWT:ValidAudience"],
ValidIssuer = configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JWT:Secret"]))
};
});
builder.Services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.Headers["Location"] = context.RedirectUri;
context.Response.StatusCode = 401;
return Task.CompletedTask;
};
});
builder.Services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder("Identity.Application", JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseRouting();
app.UseHttpsRedirection();
// Authentication & Authorization
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
Weather Forecast Controller
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet, Authorize(Roles = "Admin")]
public IEnumerable<WeatherForecast> Get()
{
var identity = HttpContext.User.Identity as ClaimsIdentity;
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
I tested your codes. The problem is you are using jwt authentication. Which require Authorize
header with value Bearer ...token...
in the request to pass the api.
But on the other hand, you are tryinng to pass the authentication with asp.net identity cookie. "JWT" and "asp.net identity cookie" are different authentication schemes. Using this cookie will never pass an "JWT" protected api.
This response means you have cookie but trying to pass it using "bearer"
An easy way to fix this is use this authorize attribute to specify the api use "asp.net identity" scheme
[Authorize(AuthenticationSchemes = "Identity.Application", Roles = "Admin")]
More over if you want this api can be pass by both "jwt" and "cookie". Modify like below:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Jwt_Or_AspIdentiyCookie", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.AuthenticationSchemes.Add("Identity.Application");
policy.RequireAuthenticatedUser();
});
});
Then in the controller use
[Authorize(policy: "Jwt_Or_AspIdentiyCookie", Roles = "Admin")]