I am adding claims to the Claims Principal Identity and signing the user in. On subsequent requests, the added claims are available in the Claims Principal anywhere in the application, but only for 25 minutes. I haven't tested between 25 and 30 minutes. After 30 minutes, the Claims Principal Identity is still authenticated but only has claims from the Identity database. The "CookieClaim" added at Login is missing. The Claims Principal Identity "IsAuthenticated" is still true, and the greeting in the Nav Menu, etc still says "Hi emailaddress." It does not matter if we make a request at 20 minutes.
I want those claims to be available for as long as the user is logged in.
The application uses OAuth to get access tokens and other information for users from several external providers, this information is used for Authorization throughout the app. I chose to put the information in claims in the cookie because it may change regularly and is not suitable for storing in the Identity database.
The following code is from the scaffold LoginModel from an asp.net core 2.2 application that I made to demonstrate. It's not exactly the same as my main app but adds the claim, type "CookieClaim" and signs in with the same methods. The four lines starting at "IdentityUser user..." are literally the only changes I've made to the template asp.net core web application (with local user accounts, after scaffolding the "Login" page)
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
IdentityUser user = await _signInManager.UserManager.FindByEmailAsync(Input.Email);
ClaimsPrincipal currentUser = await _signInManager.CreateUserPrincipalAsync(user);
currentUser.Identities.ElementAt(0).AddClaim(new Claim("CookieClaim", "I am in the Cookie"));
await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, currentUser, new AuthenticationProperties());
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
In the Identity database for this app, I have added a claim for the user, type "IdentityDatabaseClaim." This image shows the claims for the Claims Principal after logging in. This doesn't change for any request for the first 25 minutes:
After 30 minutes, the claim from the database is still there, but the claim type "CookieClaim" that was sent in the cookie at login is not, as in this screenshot:
Note that the IsAuthenticated is still true for the Claims Principal Identity at this point, all the claims from the Identity database are there.
I have tried setting the ExpireTimeSpan on the cookie in Startup.cs but this has no effect. I'm pretty sure this issue is not the cookie expiring, otherwise the user could not still be authenticated. I also tried setting IsPersistent, IssuedUtc, and ExpiresUtc on authentication properties, mostly to save anyone the trouble of offering those as answers to this question. Nothing made any difference.
I need to know what method or setting is causing the HttpContext to (seemingly) ignore data in the request cookie and create a new Claims Principal from the identity database, and whether I can a) turn it off or b) catch it working so I can save the claims before it deletes them and attach them again before it tries to get through the next Authorization filter.
The behavior only occurs when we sign in with the Application.Identity scheme (IdentityConstants.ApplicationScheme.) Use a different scheme to sign in, and any claims you add to that identity will last for as long as the cookie. So in ConfigureServices in Startup.cs:
services.AddAuthentication()
.AddCookie("CustomClaimsCookie")
Then when signing in, in the OnGetCallbackAsync() method,
ExternalLoginInfo info = await _signInManager.GetExternalLoginInfoAsync();
AuthenticationProperties props = new AuthenticationProperties();
props.StoreTokens(info.AuthenticationTokens);
await HttpContext.SignInAsync("CustomClaimsCookie", info.Principal, props);