ASP.NET Core Identity 2: User.IsInRole always returns false
The question: I call RoleManager.CreateAsync()
and RoleManager.AddClaimAsync()
to create roles and associated role claims. Then I call UserManager.AddToRoleAsync()
to add users to those roles. But when the user logs in, neither the roles nor the associated claims show up in the ClaimsPrincipal
(i.e. the Controller's User
object). The upshot of this is that User.IsInRole()
always returns false, and the collection of Claims returned by User.Claims
doesn't contain the role claims, and the [Authorize(policy: xxx)]
annotations don't work.
I should also add that one solution is to revert from using the new services.AddDefaultIdentity()
(which is provided by the templated code) back to calling services.AddIdentity().AddSomething().AddSomethingElse()
. I don't want to go there, because I've seen too many conflicting stories online about what I need to do to configure AddIdentity
for various use cases. AddDefaultIdentity
seems to do most things correctly without a lot of added fluent configuration.
BTW, I'm asking this question with the intention of answering it... unless someone else gives me a better answer than the one I'm prepared to post. I'm also asking this question because after several weeks of searching I have yet to find a good end-to-end example of creating and using Roles and Claims in ASP.NET Core Identity 2. Hopefully, the code example in this question might help someone else who stumbles upon it...
The setup:
I created a new ASP.NET Core Web Application, select Web Application (Model-View-Controller), and change the Authentication to Individual User Accounts. In the resultant project, I do the following:
In Package Manager Console, update the database to match the scaffolded migration:
update-database
Add an
ApplicationUser
class that extendsIdentityUser
. This involves adding the class, adding a line of code to theApplicationDbContext
and replacing every instance of<IdentityUser>
with<ApplicationUser>
everywhere in the project.The new
ApplicationUser
class:public class ApplicationUser : IdentityUser
public string FullName get; set;The updated
ApplicationDbContext
class:public class ApplicationDbContext : IdentityDbContext
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
// Add this line of code
public DbSet<ApplicationUser> ApplicationUsers get; set;In Package Manager Console, create a new migration and update the database to incorporate the
ApplicationUsers
entity.
add-migration m_001
update-databaseAdd the following line of code in
Startup.cs
to enableRoleManager
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();Add some code to seed roles, claims, and users. The basic concept for this sample code is that I have two claims:
can_report
allows the holder to create reports, andcan_test
allows the holder to run tests. I have two Roles,Admin
andTester
. TheTester
role can run tests, but can't create reports. TheAdmin
role can do both. So, I add the claims to the roles, and create oneAdmin
test user and oneTester
test user.First, I add a class whose sole purpose in life is to contain constants used elsewhere in this example:
// Contains constant strings used throughout this example
public class MyApp
// Claims
public const string CanTestClaim = "can_test";
public const string CanReportClaim = "can_report";
// Role names
public const string AdminRole = "admin";
public const string TesterRole = "tester";
// Authorization policy names
public const string CanTestPolicy = "can_test";
public const string CanReportPolicy = "can_report";Next, I seed my roles, claims, and users. I put this code in the main landing page controller just for expedience; it really belongs in the "startup"
Configure
method, but that's an extra half-dozen lines of code...public class HomeController : Controller
const string Password = "QwertyA1?";
const string AdminEmail = "admin@example.com";
const string TesterEmail = "tester@example.com";
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
// Constructor (DI claptrap)
public HomeController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
_roleManager = roleManager;
_userManager = userManager;
public async Task<IActionResult> Index()
// Initialize roles
if (!await _roleManager.RoleExistsAsync(MyApp.AdminRole))
var role = new IdentityRole(MyApp.AdminRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanReportClaim, ""));
if (!await _roleManager.RoleExistsAsync(MyApp.TesterRole))
var role = new IdentityRole(MyApp.TesterRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
// Initialize users
var qry = _userManager.Users;
IdentityResult result;
if (await qry.Where(x => x.UserName == AdminEmail).FirstOrDefaultAsync() == null)
var user = new ApplicationUser
UserName = AdminEmail,
Email = AdminEmail,
FullName = "Administrator"
;
result = await _userManager.CreateAsync(user, Password);
if (!result.Succeeded) throw new InvalidOperationException(string.Join("
if (await qry.Where(x => x.UserName == TesterEmail).FirstOrDefaultAsync() == null) ", result.Errors.Select(x => x.Description)));
// Roles and Claims are in a cookie. Don't expect to see them in
// the same request that creates them (i.e., the request that
// executes the above code to create them). You need to refresh
// the page to create a round-trip that includes the cookie.
var admin = User.IsInRole(MyApp.AdminRole);
var claims = User.Claims.ToList();
return View();
[Authorize(policy: MyApp.CanTestPolicy)]
public IActionResult Test()
return View();
[Authorize(policy: MyApp.CanReportPolicy)]
public IActionResult Report()
return View();
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
return View(new ErrorViewModel RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier );
and I register my authentication policies in the "Startup"
ConfigureServices
routine, just after the call toservices.AddMvc
// Register authorization policies
services.AddAuthorization(options =>
options.AddPolicy(MyApp.CanTestPolicy, policy => policy.RequireClaim(MyApp.CanTestClaim));
options.AddPolicy(MyApp.CanReportPolicy, policy => policy.RequireClaim(MyApp.CanReportClaim));
);
Whew. Now, (assuming I've noted all of the applicable code I've added to the project, above), when I run the app, I notice that neither of my "built-in" test users can access either the /home/Test
or /home/Report
page. Moreover, if I set a breakpoint in the Index method, I see that my roles and claims do not exist in the User
object. But I can look at the database and see all of the roles and claims are there.
asp.net-core asp.net-core-mvc asp.net-identity
|
show 6 more comments
The question: I call RoleManager.CreateAsync()
and RoleManager.AddClaimAsync()
to create roles and associated role claims. Then I call UserManager.AddToRoleAsync()
to add users to those roles. But when the user logs in, neither the roles nor the associated claims show up in the ClaimsPrincipal
(i.e. the Controller's User
object). The upshot of this is that User.IsInRole()
always returns false, and the collection of Claims returned by User.Claims
doesn't contain the role claims, and the [Authorize(policy: xxx)]
annotations don't work.
I should also add that one solution is to revert from using the new services.AddDefaultIdentity()
(which is provided by the templated code) back to calling services.AddIdentity().AddSomething().AddSomethingElse()
. I don't want to go there, because I've seen too many conflicting stories online about what I need to do to configure AddIdentity
for various use cases. AddDefaultIdentity
seems to do most things correctly without a lot of added fluent configuration.
BTW, I'm asking this question with the intention of answering it... unless someone else gives me a better answer than the one I'm prepared to post. I'm also asking this question because after several weeks of searching I have yet to find a good end-to-end example of creating and using Roles and Claims in ASP.NET Core Identity 2. Hopefully, the code example in this question might help someone else who stumbles upon it...
The setup:
I created a new ASP.NET Core Web Application, select Web Application (Model-View-Controller), and change the Authentication to Individual User Accounts. In the resultant project, I do the following:
In Package Manager Console, update the database to match the scaffolded migration:
update-database
Add an
ApplicationUser
class that extendsIdentityUser
. This involves adding the class, adding a line of code to theApplicationDbContext
and replacing every instance of<IdentityUser>
with<ApplicationUser>
everywhere in the project.The new
ApplicationUser
class:public class ApplicationUser : IdentityUser
public string FullName get; set;The updated
ApplicationDbContext
class:public class ApplicationDbContext : IdentityDbContext
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
// Add this line of code
public DbSet<ApplicationUser> ApplicationUsers get; set;In Package Manager Console, create a new migration and update the database to incorporate the
ApplicationUsers
entity.
add-migration m_001
update-databaseAdd the following line of code in
Startup.cs
to enableRoleManager
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();Add some code to seed roles, claims, and users. The basic concept for this sample code is that I have two claims:
can_report
allows the holder to create reports, andcan_test
allows the holder to run tests. I have two Roles,Admin
andTester
. TheTester
role can run tests, but can't create reports. TheAdmin
role can do both. So, I add the claims to the roles, and create oneAdmin
test user and oneTester
test user.First, I add a class whose sole purpose in life is to contain constants used elsewhere in this example:
// Contains constant strings used throughout this example
public class MyApp
// Claims
public const string CanTestClaim = "can_test";
public const string CanReportClaim = "can_report";
// Role names
public const string AdminRole = "admin";
public const string TesterRole = "tester";
// Authorization policy names
public const string CanTestPolicy = "can_test";
public const string CanReportPolicy = "can_report";Next, I seed my roles, claims, and users. I put this code in the main landing page controller just for expedience; it really belongs in the "startup"
Configure
method, but that's an extra half-dozen lines of code...public class HomeController : Controller
const string Password = "QwertyA1?";
const string AdminEmail = "admin@example.com";
const string TesterEmail = "tester@example.com";
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
// Constructor (DI claptrap)
public HomeController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
_roleManager = roleManager;
_userManager = userManager;
public async Task<IActionResult> Index()
// Initialize roles
if (!await _roleManager.RoleExistsAsync(MyApp.AdminRole))
var role = new IdentityRole(MyApp.AdminRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanReportClaim, ""));
if (!await _roleManager.RoleExistsAsync(MyApp.TesterRole))
var role = new IdentityRole(MyApp.TesterRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
// Initialize users
var qry = _userManager.Users;
IdentityResult result;
if (await qry.Where(x => x.UserName == AdminEmail).FirstOrDefaultAsync() == null)
var user = new ApplicationUser
UserName = AdminEmail,
Email = AdminEmail,
FullName = "Administrator"
;
result = await _userManager.CreateAsync(user, Password);
if (!result.Succeeded) throw new InvalidOperationException(string.Join("
if (await qry.Where(x => x.UserName == TesterEmail).FirstOrDefaultAsync() == null) ", result.Errors.Select(x => x.Description)));
// Roles and Claims are in a cookie. Don't expect to see them in
// the same request that creates them (i.e., the request that
// executes the above code to create them). You need to refresh
// the page to create a round-trip that includes the cookie.
var admin = User.IsInRole(MyApp.AdminRole);
var claims = User.Claims.ToList();
return View();
[Authorize(policy: MyApp.CanTestPolicy)]
public IActionResult Test()
return View();
[Authorize(policy: MyApp.CanReportPolicy)]
public IActionResult Report()
return View();
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
return View(new ErrorViewModel RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier );
and I register my authentication policies in the "Startup"
ConfigureServices
routine, just after the call toservices.AddMvc
// Register authorization policies
services.AddAuthorization(options =>
options.AddPolicy(MyApp.CanTestPolicy, policy => policy.RequireClaim(MyApp.CanTestClaim));
options.AddPolicy(MyApp.CanReportPolicy, policy => policy.RequireClaim(MyApp.CanReportClaim));
);
Whew. Now, (assuming I've noted all of the applicable code I've added to the project, above), when I run the app, I notice that neither of my "built-in" test users can access either the /home/Test
or /home/Report
page. Moreover, if I set a breakpoint in the Index method, I see that my roles and claims do not exist in the User
object. But I can look at the database and see all of the roles and claims are there.
asp.net-core asp.net-core-mvc asp.net-identity
How is the user authenticating? None of your code indicates how the app could possibly know who the current user is.
– Brad
Nov 13 '18 at 0:00
The user logs in. That is part of the code I get from the template.
– Bob.at.Indigo.Health
Nov 13 '18 at 2:11
Is there a claim containing the user id?
– Brad
Nov 13 '18 at 2:58
When a user logs in, theUser.Claims
list containsnameidentifier
(a GUID with punctuation [dashes]),name
(the username), andSecurityStamp
(a GUID without punctuation). But, because I've associated a Role with the user, and the role has associated claims, I should also see those claims in the collection. (You'll have to wait for the answer to find out why my custom claims aren't there. ;)
– Bob.at.Indigo.Health
Nov 13 '18 at 4:23
Did u try adding custom claims
– t-prisar
Nov 13 '18 at 4:38
|
show 6 more comments
The question: I call RoleManager.CreateAsync()
and RoleManager.AddClaimAsync()
to create roles and associated role claims. Then I call UserManager.AddToRoleAsync()
to add users to those roles. But when the user logs in, neither the roles nor the associated claims show up in the ClaimsPrincipal
(i.e. the Controller's User
object). The upshot of this is that User.IsInRole()
always returns false, and the collection of Claims returned by User.Claims
doesn't contain the role claims, and the [Authorize(policy: xxx)]
annotations don't work.
I should also add that one solution is to revert from using the new services.AddDefaultIdentity()
(which is provided by the templated code) back to calling services.AddIdentity().AddSomething().AddSomethingElse()
. I don't want to go there, because I've seen too many conflicting stories online about what I need to do to configure AddIdentity
for various use cases. AddDefaultIdentity
seems to do most things correctly without a lot of added fluent configuration.
BTW, I'm asking this question with the intention of answering it... unless someone else gives me a better answer than the one I'm prepared to post. I'm also asking this question because after several weeks of searching I have yet to find a good end-to-end example of creating and using Roles and Claims in ASP.NET Core Identity 2. Hopefully, the code example in this question might help someone else who stumbles upon it...
The setup:
I created a new ASP.NET Core Web Application, select Web Application (Model-View-Controller), and change the Authentication to Individual User Accounts. In the resultant project, I do the following:
In Package Manager Console, update the database to match the scaffolded migration:
update-database
Add an
ApplicationUser
class that extendsIdentityUser
. This involves adding the class, adding a line of code to theApplicationDbContext
and replacing every instance of<IdentityUser>
with<ApplicationUser>
everywhere in the project.The new
ApplicationUser
class:public class ApplicationUser : IdentityUser
public string FullName get; set;The updated
ApplicationDbContext
class:public class ApplicationDbContext : IdentityDbContext
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
// Add this line of code
public DbSet<ApplicationUser> ApplicationUsers get; set;In Package Manager Console, create a new migration and update the database to incorporate the
ApplicationUsers
entity.
add-migration m_001
update-databaseAdd the following line of code in
Startup.cs
to enableRoleManager
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();Add some code to seed roles, claims, and users. The basic concept for this sample code is that I have two claims:
can_report
allows the holder to create reports, andcan_test
allows the holder to run tests. I have two Roles,Admin
andTester
. TheTester
role can run tests, but can't create reports. TheAdmin
role can do both. So, I add the claims to the roles, and create oneAdmin
test user and oneTester
test user.First, I add a class whose sole purpose in life is to contain constants used elsewhere in this example:
// Contains constant strings used throughout this example
public class MyApp
// Claims
public const string CanTestClaim = "can_test";
public const string CanReportClaim = "can_report";
// Role names
public const string AdminRole = "admin";
public const string TesterRole = "tester";
// Authorization policy names
public const string CanTestPolicy = "can_test";
public const string CanReportPolicy = "can_report";Next, I seed my roles, claims, and users. I put this code in the main landing page controller just for expedience; it really belongs in the "startup"
Configure
method, but that's an extra half-dozen lines of code...public class HomeController : Controller
const string Password = "QwertyA1?";
const string AdminEmail = "admin@example.com";
const string TesterEmail = "tester@example.com";
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
// Constructor (DI claptrap)
public HomeController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
_roleManager = roleManager;
_userManager = userManager;
public async Task<IActionResult> Index()
// Initialize roles
if (!await _roleManager.RoleExistsAsync(MyApp.AdminRole))
var role = new IdentityRole(MyApp.AdminRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanReportClaim, ""));
if (!await _roleManager.RoleExistsAsync(MyApp.TesterRole))
var role = new IdentityRole(MyApp.TesterRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
// Initialize users
var qry = _userManager.Users;
IdentityResult result;
if (await qry.Where(x => x.UserName == AdminEmail).FirstOrDefaultAsync() == null)
var user = new ApplicationUser
UserName = AdminEmail,
Email = AdminEmail,
FullName = "Administrator"
;
result = await _userManager.CreateAsync(user, Password);
if (!result.Succeeded) throw new InvalidOperationException(string.Join("
if (await qry.Where(x => x.UserName == TesterEmail).FirstOrDefaultAsync() == null) ", result.Errors.Select(x => x.Description)));
// Roles and Claims are in a cookie. Don't expect to see them in
// the same request that creates them (i.e., the request that
// executes the above code to create them). You need to refresh
// the page to create a round-trip that includes the cookie.
var admin = User.IsInRole(MyApp.AdminRole);
var claims = User.Claims.ToList();
return View();
[Authorize(policy: MyApp.CanTestPolicy)]
public IActionResult Test()
return View();
[Authorize(policy: MyApp.CanReportPolicy)]
public IActionResult Report()
return View();
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
return View(new ErrorViewModel RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier );
and I register my authentication policies in the "Startup"
ConfigureServices
routine, just after the call toservices.AddMvc
// Register authorization policies
services.AddAuthorization(options =>
options.AddPolicy(MyApp.CanTestPolicy, policy => policy.RequireClaim(MyApp.CanTestClaim));
options.AddPolicy(MyApp.CanReportPolicy, policy => policy.RequireClaim(MyApp.CanReportClaim));
);
Whew. Now, (assuming I've noted all of the applicable code I've added to the project, above), when I run the app, I notice that neither of my "built-in" test users can access either the /home/Test
or /home/Report
page. Moreover, if I set a breakpoint in the Index method, I see that my roles and claims do not exist in the User
object. But I can look at the database and see all of the roles and claims are there.
asp.net-core asp.net-core-mvc asp.net-identity
The question: I call RoleManager.CreateAsync()
and RoleManager.AddClaimAsync()
to create roles and associated role claims. Then I call UserManager.AddToRoleAsync()
to add users to those roles. But when the user logs in, neither the roles nor the associated claims show up in the ClaimsPrincipal
(i.e. the Controller's User
object). The upshot of this is that User.IsInRole()
always returns false, and the collection of Claims returned by User.Claims
doesn't contain the role claims, and the [Authorize(policy: xxx)]
annotations don't work.
I should also add that one solution is to revert from using the new services.AddDefaultIdentity()
(which is provided by the templated code) back to calling services.AddIdentity().AddSomething().AddSomethingElse()
. I don't want to go there, because I've seen too many conflicting stories online about what I need to do to configure AddIdentity
for various use cases. AddDefaultIdentity
seems to do most things correctly without a lot of added fluent configuration.
BTW, I'm asking this question with the intention of answering it... unless someone else gives me a better answer than the one I'm prepared to post. I'm also asking this question because after several weeks of searching I have yet to find a good end-to-end example of creating and using Roles and Claims in ASP.NET Core Identity 2. Hopefully, the code example in this question might help someone else who stumbles upon it...
The setup:
I created a new ASP.NET Core Web Application, select Web Application (Model-View-Controller), and change the Authentication to Individual User Accounts. In the resultant project, I do the following:
In Package Manager Console, update the database to match the scaffolded migration:
update-database
Add an
ApplicationUser
class that extendsIdentityUser
. This involves adding the class, adding a line of code to theApplicationDbContext
and replacing every instance of<IdentityUser>
with<ApplicationUser>
everywhere in the project.The new
ApplicationUser
class:public class ApplicationUser : IdentityUser
public string FullName get; set;The updated
ApplicationDbContext
class:public class ApplicationDbContext : IdentityDbContext
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
// Add this line of code
public DbSet<ApplicationUser> ApplicationUsers get; set;In Package Manager Console, create a new migration and update the database to incorporate the
ApplicationUsers
entity.
add-migration m_001
update-databaseAdd the following line of code in
Startup.cs
to enableRoleManager
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();Add some code to seed roles, claims, and users. The basic concept for this sample code is that I have two claims:
can_report
allows the holder to create reports, andcan_test
allows the holder to run tests. I have two Roles,Admin
andTester
. TheTester
role can run tests, but can't create reports. TheAdmin
role can do both. So, I add the claims to the roles, and create oneAdmin
test user and oneTester
test user.First, I add a class whose sole purpose in life is to contain constants used elsewhere in this example:
// Contains constant strings used throughout this example
public class MyApp
// Claims
public const string CanTestClaim = "can_test";
public const string CanReportClaim = "can_report";
// Role names
public const string AdminRole = "admin";
public const string TesterRole = "tester";
// Authorization policy names
public const string CanTestPolicy = "can_test";
public const string CanReportPolicy = "can_report";Next, I seed my roles, claims, and users. I put this code in the main landing page controller just for expedience; it really belongs in the "startup"
Configure
method, but that's an extra half-dozen lines of code...public class HomeController : Controller
const string Password = "QwertyA1?";
const string AdminEmail = "admin@example.com";
const string TesterEmail = "tester@example.com";
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
// Constructor (DI claptrap)
public HomeController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
_roleManager = roleManager;
_userManager = userManager;
public async Task<IActionResult> Index()
// Initialize roles
if (!await _roleManager.RoleExistsAsync(MyApp.AdminRole))
var role = new IdentityRole(MyApp.AdminRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanReportClaim, ""));
if (!await _roleManager.RoleExistsAsync(MyApp.TesterRole))
var role = new IdentityRole(MyApp.TesterRole);
await _roleManager.CreateAsync(role);
await _roleManager.AddClaimAsync(role, new Claim(MyApp.CanTestClaim, ""));
// Initialize users
var qry = _userManager.Users;
IdentityResult result;
if (await qry.Where(x => x.UserName == AdminEmail).FirstOrDefaultAsync() == null)
var user = new ApplicationUser
UserName = AdminEmail,
Email = AdminEmail,
FullName = "Administrator"
;
result = await _userManager.CreateAsync(user, Password);
if (!result.Succeeded) throw new InvalidOperationException(string.Join("
if (await qry.Where(x => x.UserName == TesterEmail).FirstOrDefaultAsync() == null) ", result.Errors.Select(x => x.Description)));
// Roles and Claims are in a cookie. Don't expect to see them in
// the same request that creates them (i.e., the request that
// executes the above code to create them). You need to refresh
// the page to create a round-trip that includes the cookie.
var admin = User.IsInRole(MyApp.AdminRole);
var claims = User.Claims.ToList();
return View();
[Authorize(policy: MyApp.CanTestPolicy)]
public IActionResult Test()
return View();
[Authorize(policy: MyApp.CanReportPolicy)]
public IActionResult Report()
return View();
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
return View(new ErrorViewModel RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier );
and I register my authentication policies in the "Startup"
ConfigureServices
routine, just after the call toservices.AddMvc
// Register authorization policies
services.AddAuthorization(options =>
options.AddPolicy(MyApp.CanTestPolicy, policy => policy.RequireClaim(MyApp.CanTestClaim));
options.AddPolicy(MyApp.CanReportPolicy, policy => policy.RequireClaim(MyApp.CanReportClaim));
);
Whew. Now, (assuming I've noted all of the applicable code I've added to the project, above), when I run the app, I notice that neither of my "built-in" test users can access either the /home/Test
or /home/Report
page. Moreover, if I set a breakpoint in the Index method, I see that my roles and claims do not exist in the User
object. But I can look at the database and see all of the roles and claims are there.
asp.net-core asp.net-core-mvc asp.net-identity
asp.net-core asp.net-core-mvc asp.net-identity
edited Nov 13 '18 at 20:07
Bob.at.Indigo.Health
asked Nov 12 '18 at 23:17
Bob.at.Indigo.HealthBob.at.Indigo.Health
4,29184277
4,29184277
How is the user authenticating? None of your code indicates how the app could possibly know who the current user is.
– Brad
Nov 13 '18 at 0:00
The user logs in. That is part of the code I get from the template.
– Bob.at.Indigo.Health
Nov 13 '18 at 2:11
Is there a claim containing the user id?
– Brad
Nov 13 '18 at 2:58
When a user logs in, theUser.Claims
list containsnameidentifier
(a GUID with punctuation [dashes]),name
(the username), andSecurityStamp
(a GUID without punctuation). But, because I've associated a Role with the user, and the role has associated claims, I should also see those claims in the collection. (You'll have to wait for the answer to find out why my custom claims aren't there. ;)
– Bob.at.Indigo.Health
Nov 13 '18 at 4:23
Did u try adding custom claims
– t-prisar
Nov 13 '18 at 4:38
|
show 6 more comments
How is the user authenticating? None of your code indicates how the app could possibly know who the current user is.
– Brad
Nov 13 '18 at 0:00
The user logs in. That is part of the code I get from the template.
– Bob.at.Indigo.Health
Nov 13 '18 at 2:11
Is there a claim containing the user id?
– Brad
Nov 13 '18 at 2:58
When a user logs in, theUser.Claims
list containsnameidentifier
(a GUID with punctuation [dashes]),name
(the username), andSecurityStamp
(a GUID without punctuation). But, because I've associated a Role with the user, and the role has associated claims, I should also see those claims in the collection. (You'll have to wait for the answer to find out why my custom claims aren't there. ;)
– Bob.at.Indigo.Health
Nov 13 '18 at 4:23
Did u try adding custom claims
– t-prisar
Nov 13 '18 at 4:38
How is the user authenticating? None of your code indicates how the app could possibly know who the current user is.
– Brad
Nov 13 '18 at 0:00
How is the user authenticating? None of your code indicates how the app could possibly know who the current user is.
– Brad
Nov 13 '18 at 0:00
The user logs in. That is part of the code I get from the template.
– Bob.at.Indigo.Health
Nov 13 '18 at 2:11
The user logs in. That is part of the code I get from the template.
– Bob.at.Indigo.Health
Nov 13 '18 at 2:11
Is there a claim containing the user id?
– Brad
Nov 13 '18 at 2:58
Is there a claim containing the user id?
– Brad
Nov 13 '18 at 2:58
When a user logs in, the
User.Claims
list contains nameidentifier
(a GUID with punctuation [dashes]), name
(the username), and SecurityStamp
(a GUID without punctuation). But, because I've associated a Role with the user, and the role has associated claims, I should also see those claims in the collection. (You'll have to wait for the answer to find out why my custom claims aren't there. ;)– Bob.at.Indigo.Health
Nov 13 '18 at 4:23
When a user logs in, the
User.Claims
list contains nameidentifier
(a GUID with punctuation [dashes]), name
(the username), and SecurityStamp
(a GUID without punctuation). But, because I've associated a Role with the user, and the role has associated claims, I should also see those claims in the collection. (You'll have to wait for the answer to find out why my custom claims aren't there. ;)– Bob.at.Indigo.Health
Nov 13 '18 at 4:23
Did u try adding custom claims
– t-prisar
Nov 13 '18 at 4:38
Did u try adding custom claims
– t-prisar
Nov 13 '18 at 4:38
|
show 6 more comments
2 Answers
2
active
oldest
votes
So, to recap, the question asks why the code provided by the ASP.NET Core Web Application template doesn't load roles or role claims into the cookie when a user logs in.
After much Googling and experimenting, there appear to be two modifications that must be made to the templated code in order to get Roles and Role Claims to work:
First, you must add the following line of code in Startup.cs to enable RoleManager. (This bit of magic was mentioned in the OP.)
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();
But wait, there's more! According to this discussion on GitHub, getting the roles and claims to show up in the cookie involves either reverting to the service.AddIdentity
initialization code, or sticking with service.AddDefaultIdentity
and adding this line of code to ConfigureServices
:
// Add Role claims to the User object
// See: https://github.com/aspnet/Identity/issues/1813#issuecomment-420066501
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
If you read the discussion referenced above, you'll see that Roles and Role Claims are apparently kind-of-deprecated, or at least not eagerly supported. Personally, I find it really useful to assign claims to roles, assign roles to users, and then make authorization decisions based on the claims (which are granted to the users based on their roles). This gives me an easy, declarative way to allow, for example, one function to be accessed by multiple roles (i.e. all of the roles that contain the claim used to enable that function).
But you DO want to pay attention to the amount of role and claim data being carried in the auth cookie. More data means more bytes sent to the server with each request, and I have no clue what happens when you bump up against some sort of limit to the cookie size.
add a comment |
Ahh, there are some changes from ASP.NET Core version 2.0 to 2.1. AddDefaultIdentity
is the one.
I don't know where to start from your code, so, I will provide an example to create and get user role(s).
Let's create UserRoles
first:
public enum UserRoles
[Display(Name = "Quản trị viên")]
Administrator = 0,
[Display(Name = "Kiểm soát viên")]
Moderator = 1,
[Display(Name = "Thành viên")]
Member = 2
Note: You can remove the attribute Display
.
Then, we create RolesExtensions
class:
public static class RolesExtensions
public static async Task InitializeAsync(RoleManager<IdentityRole> roleManager)
foreach (string roleName in Enum.GetNames(typeof(UserRoles)))
if (!await roleManager.RoleExistsAsync(roleName))
await roleManager.CreateAsync(new IdentityRole(roleName));
Next, in the Startup.cs
class, we run it:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
RoleManager<IdentityRole> roleManager)
// other settings...
app.UseMvc(routes =>
routes.MapRoute(
name: "default",
template: "controller=Home/action=Index/id?");
);
var task = RolesExtensions.InitializeAsync(roleManager);
task.Wait();
Note: Configure
requires a returned type void
, so we need to create a task to initialize the user roles and we call Wait
method.
Do not change the returned type like this:
public async void Configure(...)
await RolesExtensions.InitializeAsync(roleManager);
Source: Async/Await - Best Practices in Asynchronous Programming
In the ConfigureServices
method, these configurations would NOT work (we cannot use User.IsInRole
correctly):
services.AddDefaultIdentity<ApplicationUser>()
//.AddRoles<IdentityRole>()
//.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
I don't know why but AddRoles
and AddRoleManager
don't support to check role for a user (User.IsInRole
).
In this case, we need to register service like this:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
By using this way, we create 3 user roles in the databse:
When register new user, we just need to call:
await _userManager.AddToRoleAsync(user, nameof(UserRoles.Administrator));
Finally, we can use [Authorize(Roles = "Administrator")]
and:
if (User.IsInRole("Administrator"))
// authorized
// or
if (User.IsInRole(nameof(UserRoles.Administrator)))
// authorized
// but
if (User.IsInRole("ADMINISTRATOR"))
// authorized
P/S: There are a lot things which need to be implement to achieve this goal. So maybe I missed something in this example.
Hey Tan, great answer. I guess I should have added that I want to use the newfangledservices.AddDefaultIdentity
rather thanservices.AddIdentity()…
because I've seen too many conflicting stories online about what fluent methods need to followAddIdentity
to get it to work for various use cases.
– Bob.at.Indigo.Health
Nov 13 '18 at 20:02
Also, I think that your call to InitializeAsync my be prone to deadlock issues in ASP.NET. Take a look at this SO answer. This issue of calling an async method from the synchronous Startup methods is why my example code put all of the seed logic into the Controller code (so I wouldn't have to include the extra code needed to do it correctly from the Startup code).
– Bob.at.Indigo.Health
Nov 13 '18 at 20:16
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53271496%2fasp-net-core-identity-2-user-isinrole-always-returns-false%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
So, to recap, the question asks why the code provided by the ASP.NET Core Web Application template doesn't load roles or role claims into the cookie when a user logs in.
After much Googling and experimenting, there appear to be two modifications that must be made to the templated code in order to get Roles and Role Claims to work:
First, you must add the following line of code in Startup.cs to enable RoleManager. (This bit of magic was mentioned in the OP.)
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();
But wait, there's more! According to this discussion on GitHub, getting the roles and claims to show up in the cookie involves either reverting to the service.AddIdentity
initialization code, or sticking with service.AddDefaultIdentity
and adding this line of code to ConfigureServices
:
// Add Role claims to the User object
// See: https://github.com/aspnet/Identity/issues/1813#issuecomment-420066501
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
If you read the discussion referenced above, you'll see that Roles and Role Claims are apparently kind-of-deprecated, or at least not eagerly supported. Personally, I find it really useful to assign claims to roles, assign roles to users, and then make authorization decisions based on the claims (which are granted to the users based on their roles). This gives me an easy, declarative way to allow, for example, one function to be accessed by multiple roles (i.e. all of the roles that contain the claim used to enable that function).
But you DO want to pay attention to the amount of role and claim data being carried in the auth cookie. More data means more bytes sent to the server with each request, and I have no clue what happens when you bump up against some sort of limit to the cookie size.
add a comment |
So, to recap, the question asks why the code provided by the ASP.NET Core Web Application template doesn't load roles or role claims into the cookie when a user logs in.
After much Googling and experimenting, there appear to be two modifications that must be made to the templated code in order to get Roles and Role Claims to work:
First, you must add the following line of code in Startup.cs to enable RoleManager. (This bit of magic was mentioned in the OP.)
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();
But wait, there's more! According to this discussion on GitHub, getting the roles and claims to show up in the cookie involves either reverting to the service.AddIdentity
initialization code, or sticking with service.AddDefaultIdentity
and adding this line of code to ConfigureServices
:
// Add Role claims to the User object
// See: https://github.com/aspnet/Identity/issues/1813#issuecomment-420066501
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
If you read the discussion referenced above, you'll see that Roles and Role Claims are apparently kind-of-deprecated, or at least not eagerly supported. Personally, I find it really useful to assign claims to roles, assign roles to users, and then make authorization decisions based on the claims (which are granted to the users based on their roles). This gives me an easy, declarative way to allow, for example, one function to be accessed by multiple roles (i.e. all of the roles that contain the claim used to enable that function).
But you DO want to pay attention to the amount of role and claim data being carried in the auth cookie. More data means more bytes sent to the server with each request, and I have no clue what happens when you bump up against some sort of limit to the cookie size.
add a comment |
So, to recap, the question asks why the code provided by the ASP.NET Core Web Application template doesn't load roles or role claims into the cookie when a user logs in.
After much Googling and experimenting, there appear to be two modifications that must be made to the templated code in order to get Roles and Role Claims to work:
First, you must add the following line of code in Startup.cs to enable RoleManager. (This bit of magic was mentioned in the OP.)
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();
But wait, there's more! According to this discussion on GitHub, getting the roles and claims to show up in the cookie involves either reverting to the service.AddIdentity
initialization code, or sticking with service.AddDefaultIdentity
and adding this line of code to ConfigureServices
:
// Add Role claims to the User object
// See: https://github.com/aspnet/Identity/issues/1813#issuecomment-420066501
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
If you read the discussion referenced above, you'll see that Roles and Role Claims are apparently kind-of-deprecated, or at least not eagerly supported. Personally, I find it really useful to assign claims to roles, assign roles to users, and then make authorization decisions based on the claims (which are granted to the users based on their roles). This gives me an easy, declarative way to allow, for example, one function to be accessed by multiple roles (i.e. all of the roles that contain the claim used to enable that function).
But you DO want to pay attention to the amount of role and claim data being carried in the auth cookie. More data means more bytes sent to the server with each request, and I have no clue what happens when you bump up against some sort of limit to the cookie size.
So, to recap, the question asks why the code provided by the ASP.NET Core Web Application template doesn't load roles or role claims into the cookie when a user logs in.
After much Googling and experimenting, there appear to be two modifications that must be made to the templated code in order to get Roles and Role Claims to work:
First, you must add the following line of code in Startup.cs to enable RoleManager. (This bit of magic was mentioned in the OP.)
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>() // <-- Add this line
.AddEntityFrameworkStores<ApplicationDbContext>();
But wait, there's more! According to this discussion on GitHub, getting the roles and claims to show up in the cookie involves either reverting to the service.AddIdentity
initialization code, or sticking with service.AddDefaultIdentity
and adding this line of code to ConfigureServices
:
// Add Role claims to the User object
// See: https://github.com/aspnet/Identity/issues/1813#issuecomment-420066501
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
If you read the discussion referenced above, you'll see that Roles and Role Claims are apparently kind-of-deprecated, or at least not eagerly supported. Personally, I find it really useful to assign claims to roles, assign roles to users, and then make authorization decisions based on the claims (which are granted to the users based on their roles). This gives me an easy, declarative way to allow, for example, one function to be accessed by multiple roles (i.e. all of the roles that contain the claim used to enable that function).
But you DO want to pay attention to the amount of role and claim data being carried in the auth cookie. More data means more bytes sent to the server with each request, and I have no clue what happens when you bump up against some sort of limit to the cookie size.
edited Nov 16 '18 at 18:58
answered Nov 13 '18 at 20:36
Bob.at.Indigo.HealthBob.at.Indigo.Health
4,29184277
4,29184277
add a comment |
add a comment |
Ahh, there are some changes from ASP.NET Core version 2.0 to 2.1. AddDefaultIdentity
is the one.
I don't know where to start from your code, so, I will provide an example to create and get user role(s).
Let's create UserRoles
first:
public enum UserRoles
[Display(Name = "Quản trị viên")]
Administrator = 0,
[Display(Name = "Kiểm soát viên")]
Moderator = 1,
[Display(Name = "Thành viên")]
Member = 2
Note: You can remove the attribute Display
.
Then, we create RolesExtensions
class:
public static class RolesExtensions
public static async Task InitializeAsync(RoleManager<IdentityRole> roleManager)
foreach (string roleName in Enum.GetNames(typeof(UserRoles)))
if (!await roleManager.RoleExistsAsync(roleName))
await roleManager.CreateAsync(new IdentityRole(roleName));
Next, in the Startup.cs
class, we run it:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
RoleManager<IdentityRole> roleManager)
// other settings...
app.UseMvc(routes =>
routes.MapRoute(
name: "default",
template: "controller=Home/action=Index/id?");
);
var task = RolesExtensions.InitializeAsync(roleManager);
task.Wait();
Note: Configure
requires a returned type void
, so we need to create a task to initialize the user roles and we call Wait
method.
Do not change the returned type like this:
public async void Configure(...)
await RolesExtensions.InitializeAsync(roleManager);
Source: Async/Await - Best Practices in Asynchronous Programming
In the ConfigureServices
method, these configurations would NOT work (we cannot use User.IsInRole
correctly):
services.AddDefaultIdentity<ApplicationUser>()
//.AddRoles<IdentityRole>()
//.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
I don't know why but AddRoles
and AddRoleManager
don't support to check role for a user (User.IsInRole
).
In this case, we need to register service like this:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
By using this way, we create 3 user roles in the databse:
When register new user, we just need to call:
await _userManager.AddToRoleAsync(user, nameof(UserRoles.Administrator));
Finally, we can use [Authorize(Roles = "Administrator")]
and:
if (User.IsInRole("Administrator"))
// authorized
// or
if (User.IsInRole(nameof(UserRoles.Administrator)))
// authorized
// but
if (User.IsInRole("ADMINISTRATOR"))
// authorized
P/S: There are a lot things which need to be implement to achieve this goal. So maybe I missed something in this example.
Hey Tan, great answer. I guess I should have added that I want to use the newfangledservices.AddDefaultIdentity
rather thanservices.AddIdentity()…
because I've seen too many conflicting stories online about what fluent methods need to followAddIdentity
to get it to work for various use cases.
– Bob.at.Indigo.Health
Nov 13 '18 at 20:02
Also, I think that your call to InitializeAsync my be prone to deadlock issues in ASP.NET. Take a look at this SO answer. This issue of calling an async method from the synchronous Startup methods is why my example code put all of the seed logic into the Controller code (so I wouldn't have to include the extra code needed to do it correctly from the Startup code).
– Bob.at.Indigo.Health
Nov 13 '18 at 20:16
add a comment |
Ahh, there are some changes from ASP.NET Core version 2.0 to 2.1. AddDefaultIdentity
is the one.
I don't know where to start from your code, so, I will provide an example to create and get user role(s).
Let's create UserRoles
first:
public enum UserRoles
[Display(Name = "Quản trị viên")]
Administrator = 0,
[Display(Name = "Kiểm soát viên")]
Moderator = 1,
[Display(Name = "Thành viên")]
Member = 2
Note: You can remove the attribute Display
.
Then, we create RolesExtensions
class:
public static class RolesExtensions
public static async Task InitializeAsync(RoleManager<IdentityRole> roleManager)
foreach (string roleName in Enum.GetNames(typeof(UserRoles)))
if (!await roleManager.RoleExistsAsync(roleName))
await roleManager.CreateAsync(new IdentityRole(roleName));
Next, in the Startup.cs
class, we run it:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
RoleManager<IdentityRole> roleManager)
// other settings...
app.UseMvc(routes =>
routes.MapRoute(
name: "default",
template: "controller=Home/action=Index/id?");
);
var task = RolesExtensions.InitializeAsync(roleManager);
task.Wait();
Note: Configure
requires a returned type void
, so we need to create a task to initialize the user roles and we call Wait
method.
Do not change the returned type like this:
public async void Configure(...)
await RolesExtensions.InitializeAsync(roleManager);
Source: Async/Await - Best Practices in Asynchronous Programming
In the ConfigureServices
method, these configurations would NOT work (we cannot use User.IsInRole
correctly):
services.AddDefaultIdentity<ApplicationUser>()
//.AddRoles<IdentityRole>()
//.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
I don't know why but AddRoles
and AddRoleManager
don't support to check role for a user (User.IsInRole
).
In this case, we need to register service like this:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
By using this way, we create 3 user roles in the databse:
When register new user, we just need to call:
await _userManager.AddToRoleAsync(user, nameof(UserRoles.Administrator));
Finally, we can use [Authorize(Roles = "Administrator")]
and:
if (User.IsInRole("Administrator"))
// authorized
// or
if (User.IsInRole(nameof(UserRoles.Administrator)))
// authorized
// but
if (User.IsInRole("ADMINISTRATOR"))
// authorized
P/S: There are a lot things which need to be implement to achieve this goal. So maybe I missed something in this example.
Hey Tan, great answer. I guess I should have added that I want to use the newfangledservices.AddDefaultIdentity
rather thanservices.AddIdentity()…
because I've seen too many conflicting stories online about what fluent methods need to followAddIdentity
to get it to work for various use cases.
– Bob.at.Indigo.Health
Nov 13 '18 at 20:02
Also, I think that your call to InitializeAsync my be prone to deadlock issues in ASP.NET. Take a look at this SO answer. This issue of calling an async method from the synchronous Startup methods is why my example code put all of the seed logic into the Controller code (so I wouldn't have to include the extra code needed to do it correctly from the Startup code).
– Bob.at.Indigo.Health
Nov 13 '18 at 20:16
add a comment |
Ahh, there are some changes from ASP.NET Core version 2.0 to 2.1. AddDefaultIdentity
is the one.
I don't know where to start from your code, so, I will provide an example to create and get user role(s).
Let's create UserRoles
first:
public enum UserRoles
[Display(Name = "Quản trị viên")]
Administrator = 0,
[Display(Name = "Kiểm soát viên")]
Moderator = 1,
[Display(Name = "Thành viên")]
Member = 2
Note: You can remove the attribute Display
.
Then, we create RolesExtensions
class:
public static class RolesExtensions
public static async Task InitializeAsync(RoleManager<IdentityRole> roleManager)
foreach (string roleName in Enum.GetNames(typeof(UserRoles)))
if (!await roleManager.RoleExistsAsync(roleName))
await roleManager.CreateAsync(new IdentityRole(roleName));
Next, in the Startup.cs
class, we run it:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
RoleManager<IdentityRole> roleManager)
// other settings...
app.UseMvc(routes =>
routes.MapRoute(
name: "default",
template: "controller=Home/action=Index/id?");
);
var task = RolesExtensions.InitializeAsync(roleManager);
task.Wait();
Note: Configure
requires a returned type void
, so we need to create a task to initialize the user roles and we call Wait
method.
Do not change the returned type like this:
public async void Configure(...)
await RolesExtensions.InitializeAsync(roleManager);
Source: Async/Await - Best Practices in Asynchronous Programming
In the ConfigureServices
method, these configurations would NOT work (we cannot use User.IsInRole
correctly):
services.AddDefaultIdentity<ApplicationUser>()
//.AddRoles<IdentityRole>()
//.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
I don't know why but AddRoles
and AddRoleManager
don't support to check role for a user (User.IsInRole
).
In this case, we need to register service like this:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
By using this way, we create 3 user roles in the databse:
When register new user, we just need to call:
await _userManager.AddToRoleAsync(user, nameof(UserRoles.Administrator));
Finally, we can use [Authorize(Roles = "Administrator")]
and:
if (User.IsInRole("Administrator"))
// authorized
// or
if (User.IsInRole(nameof(UserRoles.Administrator)))
// authorized
// but
if (User.IsInRole("ADMINISTRATOR"))
// authorized
P/S: There are a lot things which need to be implement to achieve this goal. So maybe I missed something in this example.
Ahh, there are some changes from ASP.NET Core version 2.0 to 2.1. AddDefaultIdentity
is the one.
I don't know where to start from your code, so, I will provide an example to create and get user role(s).
Let's create UserRoles
first:
public enum UserRoles
[Display(Name = "Quản trị viên")]
Administrator = 0,
[Display(Name = "Kiểm soát viên")]
Moderator = 1,
[Display(Name = "Thành viên")]
Member = 2
Note: You can remove the attribute Display
.
Then, we create RolesExtensions
class:
public static class RolesExtensions
public static async Task InitializeAsync(RoleManager<IdentityRole> roleManager)
foreach (string roleName in Enum.GetNames(typeof(UserRoles)))
if (!await roleManager.RoleExistsAsync(roleName))
await roleManager.CreateAsync(new IdentityRole(roleName));
Next, in the Startup.cs
class, we run it:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
RoleManager<IdentityRole> roleManager)
// other settings...
app.UseMvc(routes =>
routes.MapRoute(
name: "default",
template: "controller=Home/action=Index/id?");
);
var task = RolesExtensions.InitializeAsync(roleManager);
task.Wait();
Note: Configure
requires a returned type void
, so we need to create a task to initialize the user roles and we call Wait
method.
Do not change the returned type like this:
public async void Configure(...)
await RolesExtensions.InitializeAsync(roleManager);
Source: Async/Await - Best Practices in Asynchronous Programming
In the ConfigureServices
method, these configurations would NOT work (we cannot use User.IsInRole
correctly):
services.AddDefaultIdentity<ApplicationUser>()
//.AddRoles<IdentityRole>()
//.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
I don't know why but AddRoles
and AddRoleManager
don't support to check role for a user (User.IsInRole
).
In this case, we need to register service like this:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
By using this way, we create 3 user roles in the databse:
When register new user, we just need to call:
await _userManager.AddToRoleAsync(user, nameof(UserRoles.Administrator));
Finally, we can use [Authorize(Roles = "Administrator")]
and:
if (User.IsInRole("Administrator"))
// authorized
// or
if (User.IsInRole(nameof(UserRoles.Administrator)))
// authorized
// but
if (User.IsInRole("ADMINISTRATOR"))
// authorized
P/S: There are a lot things which need to be implement to achieve this goal. So maybe I missed something in this example.
answered Nov 13 '18 at 8:09
FooFoo
1
1
Hey Tan, great answer. I guess I should have added that I want to use the newfangledservices.AddDefaultIdentity
rather thanservices.AddIdentity()…
because I've seen too many conflicting stories online about what fluent methods need to followAddIdentity
to get it to work for various use cases.
– Bob.at.Indigo.Health
Nov 13 '18 at 20:02
Also, I think that your call to InitializeAsync my be prone to deadlock issues in ASP.NET. Take a look at this SO answer. This issue of calling an async method from the synchronous Startup methods is why my example code put all of the seed logic into the Controller code (so I wouldn't have to include the extra code needed to do it correctly from the Startup code).
– Bob.at.Indigo.Health
Nov 13 '18 at 20:16
add a comment |
Hey Tan, great answer. I guess I should have added that I want to use the newfangledservices.AddDefaultIdentity
rather thanservices.AddIdentity()…
because I've seen too many conflicting stories online about what fluent methods need to followAddIdentity
to get it to work for various use cases.
– Bob.at.Indigo.Health
Nov 13 '18 at 20:02
Also, I think that your call to InitializeAsync my be prone to deadlock issues in ASP.NET. Take a look at this SO answer. This issue of calling an async method from the synchronous Startup methods is why my example code put all of the seed logic into the Controller code (so I wouldn't have to include the extra code needed to do it correctly from the Startup code).
– Bob.at.Indigo.Health
Nov 13 '18 at 20:16
Hey Tan, great answer. I guess I should have added that I want to use the newfangled
services.AddDefaultIdentity
rather than services.AddIdentity()…
because I've seen too many conflicting stories online about what fluent methods need to follow AddIdentity
to get it to work for various use cases.– Bob.at.Indigo.Health
Nov 13 '18 at 20:02
Hey Tan, great answer. I guess I should have added that I want to use the newfangled
services.AddDefaultIdentity
rather than services.AddIdentity()…
because I've seen too many conflicting stories online about what fluent methods need to follow AddIdentity
to get it to work for various use cases.– Bob.at.Indigo.Health
Nov 13 '18 at 20:02
Also, I think that your call to InitializeAsync my be prone to deadlock issues in ASP.NET. Take a look at this SO answer. This issue of calling an async method from the synchronous Startup methods is why my example code put all of the seed logic into the Controller code (so I wouldn't have to include the extra code needed to do it correctly from the Startup code).
– Bob.at.Indigo.Health
Nov 13 '18 at 20:16
Also, I think that your call to InitializeAsync my be prone to deadlock issues in ASP.NET. Take a look at this SO answer. This issue of calling an async method from the synchronous Startup methods is why my example code put all of the seed logic into the Controller code (so I wouldn't have to include the extra code needed to do it correctly from the Startup code).
– Bob.at.Indigo.Health
Nov 13 '18 at 20:16
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53271496%2fasp-net-core-identity-2-user-isinrole-always-returns-false%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
How is the user authenticating? None of your code indicates how the app could possibly know who the current user is.
– Brad
Nov 13 '18 at 0:00
The user logs in. That is part of the code I get from the template.
– Bob.at.Indigo.Health
Nov 13 '18 at 2:11
Is there a claim containing the user id?
– Brad
Nov 13 '18 at 2:58
When a user logs in, the
User.Claims
list containsnameidentifier
(a GUID with punctuation [dashes]),name
(the username), andSecurityStamp
(a GUID without punctuation). But, because I've associated a Role with the user, and the role has associated claims, I should also see those claims in the collection. (You'll have to wait for the answer to find out why my custom claims aren't there. ;)– Bob.at.Indigo.Health
Nov 13 '18 at 4:23
Did u try adding custom claims
– t-prisar
Nov 13 '18 at 4:38