I'm confused by the example in the documentation here that describes how to add claims using IUserClaimsPrincipalFactory
.
The sample code shows how to extend the ApplicationUser
class:
public class ApplicationUser : IdentityUser
{
public bool IsAdmin { get; set; }
}
...and then implement a UserClaimsPrincipalFactory
that tests that property to determine which claims to add:
if (user.IsAdmin)
{
claims.Add(new Claim(JwtClaimTypes.Role, "admin"));
}
else
{
claims.Add(new Claim(JwtClaimTypes.Role, "user"));
}
It's not stated, but I think the implication is that something else (not shown) will set the IsAdmin
property for a user in the database. I think they could have made that clear. (Also, it's disappointing that the example uses roles when there's so much confusion around roles versus claims, but I digress...)
Anyway, we have added some "role" claims to the user based on the value of that new IsAdmin
property. So far, so good. What I don't understand is the next bit:
The additional claim can then be used in the app. In a Razor Page, the
IAuthorizationService
instance can be used to access the claim value.
Sounds like the Razor page is going to access our claim then - but here's the code:
@if ((await AuthorizationService.AuthorizeAsync(User, "IsAdmin")).Succeeded)
{
...
}
Is that really accessing the claim? It looks to me like it's accessing the IsAdmin
property of the user instead. I don't see how the claim we added is referenced at all - unless there's something else that's not being explained.
That overload of AuthorizeAsync
describes the last parameter as 'policyName'. Are we meant to assume that there's a policy called "IsAdmin" that checks for our new role claim?
What a terrible piece of documentation this is - and I'm ignoring the fact that it's also in the wrong place.
It's not stated, but I think the implication is that something else (not shown) will set the IsAdmin property for a user in the database.
You can set the IsAdmin where you want,For example you can set it when register.Here is a demo:
Input Model in register:
public class InputModel
{
...
public bool IsAdmin { get; set; }
}
Post handler:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email ,IsAdmin=Input.IsAdmin};
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
//await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
// $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
Is that really accessing the claim? It looks to me like it's accessing the IsAdmin property of the user instead. I don't see how the claim we added is referenced at all - unless there's something else that's not being explained.
That overload of AuthorizeAsync describes the last parameter as 'policyName'. Are we meant to assume that there's a policy called "IsAdmin" that checks for our new role claim?
IsAdmin
is a ploicy name in the code,you need to add a policy which name is IsAdmin
,and check new role claim in it.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("IsAdmin", policy => policy.RequireClaim("role", "admin"));
});
}