Is there an existing issue for this?
Describe the bug
I am trying to implement role based authentication and custom user claims in my application.
I using ASP.NET Core with React.js project template.
To generate custom user claims, I use the ApplicationUserClaimsPrincipalFactory class, which is inherited from the UserClaimsPrincipalFactory class with the GenerateClaimsAsync method overridden.
The problem is that if AddClaimsPrincipalFactory is called after AddRoles, only the FullName field is added to the ClaimsPrincipal, and if AddClaimsPrincipalFactory is called before AddRoles, then only the role field is added to the ClaimsPrincipal.
I have created repository that represent this issue.
This issue was also raised in the this issue, but I am not use Blazor and this solution not works for me.
This problem is also in this post on StackOverflow. This solution doesn't work for me either. I have implemented this in the implement-profile-service branch.
I have debugged in different steps but didn't find the place where the problem occurs, only one required claim existed in each of them.
Expected Behavior
The claims role and FullName are added to the ClaimsPrincipal of the authorized user.
Steps To Reproduce
Add support of roles in Program.cs:
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Add new custom property to the ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public string FullName { get; set; }
}
Override Register page of Identity UI using aspnet-codegenerator tool:
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet aspnet-codegenerator identity -dc SpaWithAuth.Data.ApplicationDbContext --files "Account.Register"
Add full name editor in the Register.cshtml:
...
<div class="form-floating mb-3">
<input asp-for="Input.FullName" class="form-control" aria-required="true" placeholder="Enter your full name" />
<label asp-for="Input.FullName">Full name</label>
<span asp-validation-for="Input.FullName" class="text-danger"></span>
</div>
...
Edit InputModel class in the Register.cshtml.cs:
public class InputModel
{ {
...
[Required]
public string FullName { get; set; } }
}
Edit User object creation in the OnPostAsync method in the Register.cshtml.cs:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
...
var user = CreateUser();
user.FullName = Input.FullName;
...
}
Add code for automatically adding all users in the 'SomeRole' role in the Register.cshtml.cs:
public class RegisterModel : PageModel
{
...
private readonly RoleManager<IdentityRole> _roleManager;
public RegisterModel(
...
RoleManager<IdentityRole> roleManager)
{
...
_roleManager = roleManager;
}
...
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
...
if (ModelState.IsValid)
{
...
if (result.Succeeded)
{
if (!await _roleManager.RoleExistsAsync("SomeRole"))
await _roleManager.CreateAsync(new IdentityRole("SomeRole"));
await _userManager.AddToRoleAsync(user, "SomeRole");
...
}
...
}
...
}
...
}
Edit Home.js to display JWT data:
import React, { Component } from 'react';
import authService from './api-authorization/AuthorizeService'
export class Home extends Component {
constructor(props) {
super(props);
this.state = { user: null };
}
componentDidMount() {
this.loadData();
}
async loadData() {
this.setState({ user: await authService.getUser() });
}
render() {
let { user } = this.state;
return (
<div>
<h1>Claims</h1>
{JSON.stringify(user)}
</div>
);
}
}
Add roles and custom profile claim in the client scopes in the Program.cs:
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources.Add(new IdentityResource("roles", "Roles", new[] { JwtClaimTypes.Role, ClaimTypes.Role }));
options.IdentityResources.Add(new IdentityResource("custom", "Custom profile data", new[] { nameof(ApplicationUser.FullName) }));
options.Clients.ToList().ForEach(c =>
{
c.AllowedScopes.Add("roles");
c.AllowedScopes.Add("custom");
});
});
Add ApplicationUserClaimsPrincipalFactory class:
public class ApplicationUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser>
{
public ApplicationUserClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, optionsAccessor) { }
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
{
var identity = await base.GenerateClaimsAsync(user);
identity.AddClaim(new Claim(nameof(ApplicationUser.FullName), user.FullName));
return identity;
}
}
Add ApplicationUserClaimsPrincipalFactory usage in the services configuration in the Program.cs:
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<ApplicationUserClaimsPrincipalFactory>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Exceptions (if any)
No response
.NET Version
7.0.100
Anything else?
No response
Is there an existing issue for this?
Describe the bug
I am trying to implement role based authentication and custom user claims in my application.
I using ASP.NET Core with React.js project template.
To generate custom user claims, I use the
ApplicationUserClaimsPrincipalFactoryclass, which is inherited from theUserClaimsPrincipalFactoryclass with theGenerateClaimsAsyncmethod overridden.The problem is that if
AddClaimsPrincipalFactoryis called afterAddRoles, only theFullNamefield is added to theClaimsPrincipal, and ifAddClaimsPrincipalFactoryis called beforeAddRoles, then only therolefield is added to theClaimsPrincipal.I have created repository that represent this issue.
This issue was also raised in the this issue, but I am not use Blazor and this solution not works for me.
This problem is also in this post on StackOverflow. This solution doesn't work for me either. I have implemented this in the
implement-profile-servicebranch.I have debugged in different steps but didn't find the place where the problem occurs, only one required claim existed in each of them.
Expected Behavior
The claims
roleandFullNameare added to theClaimsPrincipalof the authorized user.Steps To Reproduce
Add support of roles in Program.cs:
Add new custom property to the ApplicationUser class:
Override Register page of Identity UI using aspnet-codegenerator tool:
Add full name editor in the Register.cshtml:
Edit InputModel class in the Register.cshtml.cs:
Edit User object creation in the OnPostAsync method in the Register.cshtml.cs:
Add code for automatically adding all users in the 'SomeRole' role in the Register.cshtml.cs:
Edit Home.js to display JWT data:
Add roles and custom profile claim in the client scopes in the Program.cs:
Add ApplicationUserClaimsPrincipalFactory class:
Add ApplicationUserClaimsPrincipalFactory usage in the services configuration in the Program.cs:
Exceptions (if any)
No response
.NET Version
7.0.100
Anything else?
No response