Search code examples
asp.net-coreasp.net-core-identity

Using User.IsInRole returns random result when UserRole changes


I'm using asp.net core 2.1 with default identity settings and every time a role is changed the user should re-login to see the role changes.

If I add the following settings, roles should be updated on each request, the Authorize attribute works well but User.IsInRole() method returns random results on each request.

services.Configure<SecurityStampValidatorOptions>(options =>
{
     options.ValidationInterval = TimeSpan.Zero;
});

What's the problem with User.IsInRole()? How to fix it?

Edit: I was incorrect about the Authorize attribute behavior, that works random too.

Edit:
a test project to reproduce the issue (warning: it's using InMemory db) -> https://github.com/ans-ashkan/AspNetCoreUserIsInRoleTest

http://localhost:5000/users/getall -> 200: ["test"]
http://localhost:5000/users/signin?username=test&password=123 -> 200
http://localhost:5000/users/isinrole?role=admin -> {"isInRole":false,"identityName":"test"}
http://localhost:5000/users/adduserrole?username=test&role=admin -> 200
http://localhost:5000/users/isinrole?role=admin -> {"isInRole":**random true or false**,"identityName":"test"}
http://localhost:5000/users/signout -> 200
http://localhost:5000/users/signin?username=test&password=123 -> 200
http://localhost:5000/users/isinrole?role=admin -> {"isInRole":true,"identityName":"test"}

Edit: Issue link on AspNet/MVC repo


Solution

  • I’ve investigated this problem in detail and posted my findings in the issue. It is indeed a bug, but it actually does not have that much of an effect on your problem:

    Changing a user’s roles will not invalidate the user’s security stamp, so the issued claims identity that the user receives through the cookie is not actually invalid. So if you want to invalidate the identity when the roles change, you will have to look for a different solution.

    However, in my opinion, you are completely misusing this: If you want to refresh the identity and its claims on every single request, then there is really no point in actually having a claims identity at all. Running the authentication stack is not free, and having to run the whole pipeline of validating and reissuing will be quite expensive. And when you don’t actually want to store the identity for a longer time anyway (because you invalidate it right in the next request), then that’s really wasted work.

    So if you really need a permission system that are absolutely sharp and updated right in the moment when they change, then consider using something different. You could set up a separate database and just store the “roles” there, and then, when you access something protected, you just fetch the user’s roles on demand there to verify access. That will also help you from fetching the roles all the time on every request since now you would only fetch it when you need it.

    Of course, you don’t need a separate database for this. You could also use the built-in roles of Identity. You just need to remember then though that the role claims are not always the source of truth, so you should always load the user’s roles from the database (through the user manager).

    You can actually design this pretty well with ASP.NET Core’s authorization stack. For example, you could create a requirement for a role and then implement an authorization handler that checks the role by going through the database. That way, you can make this as transparent as using role claims for users. E.g. you could just use the same Authorize attribute which you would be using otherwise.