You need get valid claims when generating JWT. Here is example code:
Login logic:
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login([FromBody] ApplicationUser applicationUser) {
var result = await _signInManager.PasswordSignInAsync(applicationUser.UserName, applicationUser.Password, true, false);
if(result.Succeeded) {
var user = await _userManager.FindByNameAsync(applicationUser.UserName);
// Get valid claims and pass them into JWT
var claims = await GetValidClaims(user);
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
//...
} else {
throw new ApiException('Wrong username or password', 403);
}
}
Get user claims based UserRoles
, RoleClaims
and UserClaims
tables (ASP.NET Identity):
private async Task<List<Claim>> GetValidClaims(ApplicationUser user)
{
IdentityOptions _options = new IdentityOptions();
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
new Claim(_options.ClaimsIdentity.UserIdClaimType, user.Id.ToString()),
new Claim(_options.ClaimsIdentity.UserNameClaimType, user.UserName)
};
var userClaims = await _userManager.GetClaimsAsync(user);
var userRoles = await _userManager.GetRolesAsync(user);
claims.AddRange(userClaims);
foreach (var userRole in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole));
var role = await _roleManager.FindByNameAsync(userRole);
if(role != null)
{
var roleClaims = await _roleManager.GetClaimsAsync(role);
foreach(Claim roleClaim in roleClaims)
{
claims.Add(roleClaim);
}
}
}
return claims;
}
In Startup.cs
please add needed policies into authorization:
void ConfigureServices(IServiceCollection service) {
services.AddAuthorization(options =>
{
// Here I stored necessary permissions/roles in a constant
foreach (var prop in typeof(ClaimPermission).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))
{
options.AddPolicy(prop.GetValue(null).ToString(), policy => policy.RequireClaim(ClaimType.Permission, prop.GetValue(null).ToString()));
}
});
}
ClaimPermission:
public static class ClaimPermission
{
public const string
CanAddNewService = "Tự thêm dịch vụ",
CanCancelCustomerServices = "Hủy dịch vụ khách gọi",
CanPrintReceiptAgain = "In lại hóa đơn",
CanImportGoods = "Quản lý tồn kho",
CanManageComputers = "Quản lý máy tính",
CanManageCoffees = "Quản lý bàn cà phê",
CanManageBillards = "Quản lý bàn billard";
}
Use the similar snippet to get all pre-defined permissions and insert it to asp.net permission claims table:
var staffRole = await roleManager.CreateRoleIfNotExists(UserType.Staff);
foreach (var prop in typeof(ClaimPermission).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy))
{
await roleManager.AddClaimIfNotExists(staffRole, prop.GetValue(null).ToString());
}
I am a beginner in ASP.NET, so please let me know if you have better solutions.
And, I don't know how worst when I put all claims/permissions into JWT. Too long? Performance ? Should I store generated JWT in database and check it later for getting valid user's roles/claims?