So, I did a simple Jwt token authorization. When there were no roles, everything worked. However, when I tried to make the method accessible only by role, it displays 403 and other access errors.
My Program.cs:
var builder = WebApplication.CreateBuilder(args);
var Origins = "restaraunt-react-ap";
builder.Services.AddTransient<ICheckLoginService, CheckLoginService>();
builder.Services.AddTransient<IRestaurantTPDbContext, RestaurantTPDbContext>();
builder.Services.AddTransient<IJWTService, JWTService>();
builder.Services.AddTransient<IRoleService, RoleService>();
builder.Services.AddDefaultIdentity<IdentityUser>().AddRoles<IdentityRole>().AddEntityFrameworkStores<RestarauntTPDBIdentityDBContext>();
builder.Services.AddAuthorization(option =>
{
option.AddPolicy("admin", policyBuilder => policyBuilder.RequireClaim("admin", "waiter", "cook"));
});
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = AuthOptions.ISSUER,
ValidateAudience = true,
ValidAudience = AuthOptions.AUDIENCE,
ValidateLifetime = true,
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
ValidateIssuerSigningKey = true,
};
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
options.AddPolicy(Origins, policy =>
{ policy.WithOrigins("http://localhost:3000/").AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin();
}));
builder.Services.AddDbContext<RestaurantTPDbContext>(options => options.UseSqlServer("Server = NANOMACHINE; Database = RestaurantTP; Trusted_Connection=True; TrustServerCertificate=true;"));
builder.Services.AddDbContext<RestarauntTPDBIdentityDBContext>(options => options.UseSqlServer("Server = NANOMACHINE; Database = RestaurantTP; Trusted_Connection=True; TrustServerCertificate=true;"));
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseCors(Origins);
app.MapControllers();
app.Run();
New DbContext:
public class RestarauntTPDBIdentityDBContext : IdentityDbContext<IdentityUser>
{
public DbSet<IdentityRole> roles { get; set; }
public RestarauntTPDBIdentityDBContext(DbContextOptions<RestarauntTPDBIdentityDBContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server = NANOMACHINE; Database = RestaurantTP; Trusted_Connection=True; TrustServerCertificate=true;");
}
}
My RoleService
public class RoleService : IRoleService
{
private readonly IServiceProvider _serviceProvider;
public RoleService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task SetRoles()
{
var roleManager = _serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string[] roleNames = { "administrator", "cook", "waiter" };
foreach (var roleName in roleNames)
{
bool roleExists = await roleManager.RoleExistsAsync(roleName);
if (!roleExists)
{
await roleManager.CreateAsync(new IdentityRole(roleName));
}
}
}
}
Token generator
public class JWTService : IJWTService
{
public readonly IRoleService _roleService;
public JWTService(IRoleService roleService)
{
_roleService = roleService;
}
public string GenerateToken(string name, string role)
{
var claims = new List<Claim> { new Claim(ClaimsIdentity.DefaultNameClaimType, name, ClaimsIdentity.DefaultRoleClaimType, role) };
var jwt = new JwtSecurityToken(
issuer: AuthOptions.ISSUER,
audience: AuthOptions.AUDIENCE,
claims: claims,
expires: DateTime.Now.AddSeconds(20),
signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
}
My controller
[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
private readonly ICheckLoginService _checkLoginService;
private readonly IRoleService _roleService;
public AuthenticationController(ICheckLoginService checkLoginService, IRoleService roleService)
{
_checkLoginService = checkLoginService;
_roleService = roleService;
}
[HttpGet]
[Route("gettest")]
public IActionResult GetData()
{
var data = new { Message = "Hello from ASP.NET Core Web API" };
return Ok(data);
}
[HttpPost]
[Route("sendData")]
public async Task<IActionResult> TryLogin([FromBody] AutRequest autRequest)
{
await Task.Run(() => _roleService.SetRoles());
var validate = _checkLoginService.Login(autRequest);
return Ok(validate);
}
[HttpGet]
[Authorize(Roles = "admin")]
[Route("getSecretInfo")]
public IActionResult GetSecretInfo()
{
return Ok("QWERTY");
}
public record AutRequest(string name, string password);
}
I created a new DBContext and registered the roles in the database (with my RoleService), however it didn't work :(
I don’t quite understand the role of the new DBContext, but the roles are registered there. I also checked that other requests to the server work, but it is the methods that require authorization AND a role that do not work.
public string GenerateToken(string name, string role)
{
var claims = new List<Claim> { new Claim(ClaimsIdentity.DefaultNameClaimType, name, ClaimsIdentity.DefaultRoleClaimType, role) };
var jwt = new JwtSecurityToken(
issuer: AuthOptions.ISSUER,
audience: AuthOptions.AUDIENCE,
claims: claims,
expires: DateTime.Now.AddSeconds(20),
signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
In this method you are creating a list of claims containing only one claim of type ClaimsIdentity.DefaultNameClaimType
using this constructor, which I believe is not doing what you expect it to do. Instead, you should have a separate claim for each piece of information you want to pass within the token.
So, your method should look like the following:
public string GenerateToken(string name, string role)
{
var claims = new List<Claim>
{
new Claim(ClaimsIdentity.DefaultNameClaimType, name),
new Claim(ClaimsIdentity.DefaultRoleClaimType, role)
};
var jwt = new JwtSecurityToken(
issuer: AuthOptions.ISSUER,
audience: AuthOptions.AUDIENCE,
claims: claims,
expires: DateTime.Now.AddSeconds(20),
signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
After such modification you will indeed have the role claim written in your token and authorization should work properly.