Update:It is not just the Admin role which is not working - it seems any route which required authorisation is returning a 401.
I want to create an admin role to control access to my AdminController. My stack is MongoDb/.NET Core(3.1) for the API/ Angular 9 front end.
I seed my database with the roles
private static void SeedRoles(RoleManager<MongoRole> roleManager)
{
if (!roleManager.RoleExistsAsync("User").Result)
{
MongoRole role = new MongoRole();
role.Name = "User";
IdentityResult roleResult = roleManager.
CreateAsync(role).Result;
}
if (!roleManager.RoleExistsAsync("Admin").Result)
{
MongoRole role = new MongoRole();
role.Name = "Admin";
IdentityResult roleResult = roleManager.
CreateAsync(role).Result;
}
}
In another seed method I've added the following 2 roles to my user account
userManager.AddToRoleAsync(user, "User").Wait();
userManager.AddToRoleAsync(user, "Admin").Wait();
In my startup file I've configured my mongo identity provider
services.AddIdentityMongoDbProvider<AspNetCore.Identity.Mongo.Model.MongoUser, AspNetCore.Identity.Mongo.Model.MongoRole>(identityOptions =>
{
identityOptions.Password.RequiredLength = 6;
identityOptions.Password.RequireLowercase = false;
identityOptions.Password.RequireUppercase = false;
identityOptions.Password.RequireNonAlphanumeric = false;
identityOptions.Password.RequireDigit = false;
}, mongoIdentityOptions => {
mongoIdentityOptions.ConnectionString = **REMOVED CONN STR FROM HERE**;
});
My login method in the user controller gets the roles from my user and adds them to a claim list - as far as I'm aware this is so it can be included within the token which can be checked for roles. When I debugged and added a break point on this method it was clear that the roles are being added to the claimList - so I'm not sure the problem is there.
// POST api/user/login
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Login([FromBody]LoginEntity model)
{
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, false, false);
if (result.Succeeded)
{
string key = model.UserName + "ezgig321";
var appUser = _userManager.Users.SingleOrDefault(r => r.UserName == model.UserName);
var issuer = "ezgig";
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var roles = await _userManager.GetRolesAsync(appUser);
var claimList = new List<Claim>();
foreach (var role in roles)
{
var roleClaim = new Claim(ClaimTypes.Role, role);
claimList.Add(roleClaim);
}
claimList.Add(new Claim("username", model.UserName));
//var token = AuthenticationHelper.GenerateJwtToken(model.Email, appUser, _configuration);
var token = new JwtSecurityToken(issuer, //Issure
issuer, //Audience
claimList,
expires: DateTime.Now.AddDays(1),
signingCredentials: credentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(token);
var rootData = new LoginResponse(encodedJwt, appUser.UserName);
return Ok(rootData);
}
return StatusCode((int)HttpStatusCode.Unauthorized, "Bad Credentials");
}
string errorMessage = string.Join(", ", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage));
return BadRequest(errorMessage ?? "Bad Request");
}
Yet when I use the JWT returned from logging in to my account with the admin role - I still get a 401 unauthorised when I'm trying to access this test method I've put in my admin controller.
[Authorize(Roles ="Admin")]
[Route("api/[controller]/[action]")]
public class AdminController : Controller
{
// GET api/admin/admintest
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet]
public ActionResult AdminTest()
{
return Ok("you seem to have admin authorisation");
}
It turns out it was just stupidity which caused this error. I'd hardcoded in the issuer and JWT key variables of the Register/Login end point, and I'd wrote them in incorrectly.
Because they then didn't match with the issuer/jwt key in the startup.cs file(see below)...
services.AddAuthentication(options =>
{
//Set default Authentication Schema as Bearer
options.DefaultAuthenticateScheme =
JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme =
JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters =
new TokenValidationParameters
{
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtIssuer"],
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
ClockSkew = TimeSpan.Zero // remove delay of token when expire
};
});
This meant that the JWT keys were being rejected as invalid. Sorry if anyone wasted any time on this.