For starters, this question was also asked here (Temporary Session Based Claims in ASP.NET Core Identity) and here (How to add claim to user dynamically?), but neither question received an answer so I am trying to revive it...
I am creating a multi-tenant web app. A user logs in, and they can see data related to their own company (but not the data of other companies who use the app). Because of franchise groups with multiple storefronts, etc, it is not uncommon for a single user to require access to several different companies, but in this case, they must choose a single company when logging in.
Nearly all data queries require a company ID as a parameter, so I need a convenient way of checking which company the user is currently logged into. If they log out and log into a different company, I need to see a different company ID. I would like to store it as an identity claim that I can see for the duration of the session, but I don't necessarily want to store it to the database since it may change with every login.
Here is my Login action (based on the standard ASP.Net Core MVC template):
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
// BEGIN CUSTOM CODE - Check which companies a user can access
IEnumerable<int> allCompanies = _myDbService.GetUserCompanies(model.Email);
if (allCompanies.Count() == 1)
{
// then they can only access one company - log them into it automatically.
// I need easy access to its ID with the User since it is used with almost every db query.
int companyID = allCompanies[0];
((ClaimsIdentity)User.Identity).AddClaim(new Claim("CompanyID", companyID.ToString())); // this shows as null on future controller actions.
Debug.WriteLine("Set breakpoint here to examine User"); // I see 1 claim (my custom claim) in the debugger, but it is not in the database yet.
// future controller actions show me 3 claims (nameidentifier, name, and security stamp) but User.Claims.First(c => c.Type == "CompanyID").Value throws error.
return RedirectToLocal(returnUrl);
}
else
{
// the user has access to several companies - make them choose one to complete login process
RedirectToAction("ChooseCompany", new { companyList = allCompanies });
}
// END CUSTOM CODE
}
// REMAINING CODE OMITTED FOR BREVITY
}
// If we got this far, something failed, redisplay form
return View(model);
}
So my questions are: why doesn't the custom claim "stick" so that it can be seen in future controller actions? Why isn't it saved to the database if that is the standard behavior? Is there a way to keep this value around for the duration of the session without using AddSession()? If I implement this as a claim, am I making a round trip to the database every time I access the value?