CookieAuthenticationProvider and user session invalidation on change of SecurityStamp

Another question I’ve been asked about Identity.

Part of Startup class for Owin can be this:

public void ConfigureAuth(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        Provider = new CookieAuthenticationProvider
        {
            OnValidateIdentity = SecurityStampValidator
                .OnValidateIdentity<UserManager, ApplicationUser, int>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                    getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))
        },
    });
}

The question was “How does SecurityValidator.OnValidateIdentity invalidate all existing cookies” and “I understand that getUserIdCallback delegate returns an id of a user, but I don’t quite see the usefulness of this parameter” and “why the need for regenerateIdentityCallback parameter“.

regenerateIdentityCallback is a delegate taking UserManager and ApplicationUser objects as parameters and returns a new ClaimsIdentity object. Seems strange to have a separate callback for that because default implementation of UserManager already has CreateIdentityAsync which does work for us. But signature of that method looks like this:

public virtual Task<ClaimsIdentity> CreateIdentityAsync(TUser user, string authenticationType)

See the authentication type as a parameter? without having a global setting for what type of authentication we use, there is no way know what authentication we use. So part of that delegate somehow should be authentication type.

Regarding getUserIdCallback – things are simple here. UserId is stored in cookie as a claim. But it is stored as a string. All claims are stored as strings. If you use default type for the key (String), then no problem here. But if you use Guid or int for primary key type, things get more complex. So this peace of code needs to know how to convert String with id into Guid or int.

To answer very first question “How does SecurityValidator.OnValidateIdentity invalidate all existing cookies“. Well.. it does not really invalidates all the cookies. Cookie contains issued date. On every request SecurityValidator compares if enough time have passed from cookie issue (30 minutes in the sample code above). Then it gets a UserId (via getUserIdCallback delegate) and SecurityStamp values from the cookie. Reaches into the database, finds the user and checks if SecurityStamp have been changed since the cookie have been set.
If security stamp is not changed, fresh cookie is created with new issue date. If stamp have been changed, user is logged out.

And this happens on every request. So it could be that user have logged in Firefox, a minute after security stamp have been updated for that user. User will not be logged out from Firefox for another 29 minutes. And imagine that session in Chrome was started 29 minutes before change of security stamp, so cookie in Chrome will be invalidated in 1 minute.

UserManager registration

If you notice regenerateIdentityCallback is using UserManager and ApplicationUser as part of the callback:

regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager)

How does it get an instance of UserManager? OwinContext is used for that:

var manager = context.OwinContext.GetUserManager<TManager>();

How does OwinContext know about where to get an instance of UserManager? Good question!
You must tell OwinContext how to create UserManager:

app.CreatePerOwinContext(() => new UserManager(new MyDbContext()));

Or if you are using DI in your MVC application:

app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<UserManager>());

Or if you are using standard MVC template from Visual Studio 2013:

app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

CreatePerOwinContext function registers a callback in a dictionary. And once this call returns an instance, this instance is saved in a Dictionary<String, object> for later user. Basically OWIN has it’s own little DI-container where it keeps references to objects that should be singletons during the request.

If cookie validation function can’t get an instance of UserManager, it will not be able to compare the security stamp in cookie with security stamp in the database and the CookieValidator won’t be able to invalidate the cookie. So your code should look like this:

public void ConfigureAuth(IAppBuilder app)
{
    app.CreatePerOwinContext(() => new UserManager(new MyDbContext()));

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        Provider = new CookieAuthenticationProvider
        {
            OnValidateIdentity = SecurityStampValidator
                .OnValidateIdentity<UserManager, ApplicationUser, int>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                    getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))
        },
        // other configurations
    });

    // other stuff
}