I'm a very new to ASP .NET Core Identity so if some additional info is needed to investigate the problem - feel free to ask me (Program.cs, Startup.cs or maybe some other info).
So, I have ASP .NET Core 5.0 Razor Pages Web Application where I have:
Class for User that extends Microsoft.AspNetCore.Identity.IdentityUser :
public partial class User: IdentityUser<Int64>
{
[Required]
public string Surname { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Int64 RoleId { get; set; }
public virtual Role Role { get; set; }
}
Class for UserRole that extends Microsoft.AspNetCore.Identity.IdentityRole :
public class UserRole : IdentityRole<Int64>
{
[Required]
public string Notes { get; set; }
}
DbContext extends Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext:
public partial class MyDbContext: IdentityDbContext<User, UserRole, Int64>
{
public virtual DbSet<Some_other_class_in_MyDbContext> Some_other_class { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
....
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//To get rid of an error - "The entity type 'IdentityUserLogin<long>' requires a primary key to be defined"
base.OnModelCreating(modelBuilder);
....
}
}
I've scaffolded default Identity pages (Register, Login, User data edit, etc.) and added some more pages for displaying some user-related diagrams.
After that I simply use:
- Microsoft.AspNetCore.Identity.SignInManager for Login, Logout and check whether user is logged-in (PasswordSignInAsync(), SignOutAsync() and IsSignedIn())
- Microsoft.AspNetCore.Identity.UserManager to create user, assign user to role and get current user (CreateAsync(), AddToRoleAsync() and GetUserId\GetUserAsync())
Most of that functions use Microsoft.AspNetCore.Mvc.Razor.ClaimsPrincipal User
(the comment fo this field is - Gets the System.Security.Claims.ClaimsPrincipal of the current logged in user). And I suppose user identification to work just out-of-the-box.
But while performing some concurrency tests for working with different users that are signed-in at the same time (several browsers on same PC or several PC's connection same web server) - I've faced a user identifying problem. If two users sing-in to different login accounts at almost same time they can both have same value in Microsoft.AspNetCore.Mvc.Razor.ClaimsPrincipal User
variable (Identity system recognize them as same user). I definitelly know that because each page in my application has User Name and Surname on the top of it. If second user (who has incorrect value in variable) after that refreshes the page he is currently on - he obtains correct value in variable. Very strange...
My application has over 40 different pages. I can't even imagine that I need to have some collection to persist each page's current state for each logged-in user. For me It's a huge overkill - as I said earlier, I expected Identity to just work out-of-the-box. There are several tables in my database (AspNetRoleClaims, AspNetUserClaims, AspNetUserLogins, AspNetUserClaims) that I've expected to hold all necessary data for this system to work.
So looks like I'm doing something wrong way. Any help is appreciated. Thank you in advance.
UPDATE: @AgaveJoe @Bruce (SqlWork.com)
This symptom could indicate a bug in the application or a bug in the test
Most likely a coding error on your part. Do you store user in a static?
Looks like I've found the source of the problem: Identity system itself is working correctly but I store some data (for example user FullName) in a page model's static variables that I obtain something like that:
public void OnGet()
{
var userId = _userManager.GetUserId(User);
if (userId != null)
{
var user = _context.Users.Include(u => u.Role).Single(u => u.Id == long.Parse(userId));
//filling static variable
userFullName = $"{user.Name} {user.Surname}";
....
{
}
On my page there is a menu (html select
item) and when the item is changed I send a POST request to recalculate some data on the page. Page model constructor is called and userFullName
becomes null
but OnGet
is NOT fired afterwards and therefore userFullName
is NOT filled. To preserve some data between requests I decided to use static variables. To tell you the truth I was adviced to use static variables in this case on StackOveflow.
Now I see that it was a bad decision for a multi-user application. So my questions are:
- How can I persist some data between requests?
a) As far as I know ViewData
, ViewBag
, and TempData
are destroyed at the end of each request.
b) sessionStorage
is accessible from JavaScript but I prefer pure c# solution.
c) I cannot use page model constructor because Microsoft.AspNetCore.Mvc.Razor.ClaimsPrincipal User
is null
there.
d) I suppose I can perform a check and fill in all the required data directly on the ASP Razor page (inside @{ ... }
tag) - but is it a good practice and recommended in production?
- What is the best practice to obtain current user:
a) var userId = _userManager.GetUserId(User); var user = _context.Users.Include(u => u.Role).Single(u => u.Id == long.Parse(userId));
b) var user = _userManager.GetUserAsync(User).Result;
c) var user = await _userManager.GetUserAsync(User);
Any help is appreciated. Thank you in advance.