Search code examples
.net-coreidentity.net-core-3.1

Converted Users are logged out after 30 min


I've just taken a .NET Framework application and converted it to .NET Core. When importing the data from the old User-table, the SecurityStamp values were lower-case GUIDs, e.g. '0e124deb-8392-4dcc-bce7-38dcc48569a2'. When a user change their password, they get a new SecurityStamp. Now they are uppercase, e.g. 'WHBXXXSQEIDVA7KF3T6AJJJ3AHWWUSYE'.

The users with the new SecurityStamp do not have any problems. Should I just wipe the SecurityStamp column in the user table? Is there a new SecurityStamp created when the user log in the next time? I've had a hard time finding documentation on Identity on this level.


Solution

  • A new security stamp is only created if the user change their password or unlink from an external login. Cookie is validated on a time interval of 30 mins by default. since you are using the latest .net-core version you may use the following code snippet in ConfigureServices() in startup.cs to extend your validation time.

    If time is set to 0 it will validate in every request

    
    services.Configure<SecurityStampValidatorOptions>(options =>
    {
        // This is the key to control how often validation takes place
        options.ValidationInterval = TimeSpan.FromMinutes(30);
    });
    
    

    Note: UserManager allows you to update your security stamp using the method userManager.UpdateSecurityStampAsync(user). if you use this after login, validation will fail most probably.

    Finally if you want to handle this behavior in your own way, you can write your own validator and wireup in the middleware

    
    services.AddAuthentication(options =>
    {
      options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
      options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
      options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    }).AddCookie(options =>
     {
        options.Events.OnValidatePrincipal = LastChangedValidator.ValidateAsync;
     });
    
    
    
    public static class LastChangedValidator
    {
        public static async Task ValidateAsync(CookieValidatePrincipalContext context)
        {
           // you can use your own logic 
    
           /* var userRepository = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
            var userPrincipal = context.Principal;
    
            // Look for the last changed claim.
            string lastChanged;
            lastChanged = (from c in userPrincipal.Claims
                           where c.Type == "LastUpdated"
                           select c.Value).FirstOrDefault();
    
            if (string.IsNullOrEmpty(lastChanged) ||
                !userRepository.ValidateLastChanged(userPrincipal, lastChanged))
            {
                context.RejectPrincipal();
                await context.HttpContext.Authentication.SignOutAsync("MyCookieMiddlewareInstance");
            } */
        }
    }
    
    

    Or else you can handle the ValidatePrincipal() by overriding the method using the following

    
    public class CustomCookieHandler: CookieAuthenticationEvents
    {
      public override Task ValidatePrincipal(CookieValidatePrincipalContext context)
      {
        return base.ValidatePrincipal(context);
      }
    }