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
}
  • JustMe

    I can’t find any other online articles covering this topic in as much depth as yours do, so kudos mate

  • TheBigRic

    Hi,

    I came to this post through a question on stackoverflow (https://stackoverflow.com/questions/25785491/asp-net-identity-2-1-inside-mvc5-application-using-unity) and read your comments about still having to register the usermanager with Owin when using a DI container. I was a bit suprised by this comment and to be frank, my first reaction was: ‘that cannot be true’. But after reading your great blogpost and used reflector to do some nosing in the various assemblies from asp.net identity I found out your answer is spot on. But now the big question comes: I worked out the post for simpleinjector (https://simpleinjector.codeplex.com/discussions/564822) and there I give people the advise to remove the registrations in Owin. That is obviously not good, but:
    I came to this post on SimpleInjector from a real world project, which is working. How can that be…? Maybe we can discuss further through PM. Thank in advance for your reaction.

    • I found only one place where Owin registration of UserManager is required – cookie invalidation for the case when security stamp has been changed. And even if UserManager is not registered with Owin, it does not fail, but invalidation does not work either. Also I have been looking on latest version of Identity v2.1 and it is required there. Possibly in prior versions this was not required – I can’t be sure about it.

      I understand your puzzlement – removing Owin registration was my first reaction as well when I put DI into project with Identity. But after trying to debug not-working cookie invalidation the above conclusion came to light.

      Regarding your working project – does it _really_ invalidate cookie with no Owin registration?

      • TheBigRic

        Well, after reading all of this I’m not sure at all… I really have to check that. Thanx for pointing it out to me! I let you know what the outcome is. In your comment on stackoverflow you mention that Owin also needs a registration for ApplicationDbContext. I don’t see that here and also I did not find it anywhere, while searching with reflector. So why and where is the context needed?

        • ApplicationDbContext is not strictly required.
          In standard VS template UserManager is requiring AppDbContext and standard implementation UserManager.Create() uses Owin registration to resolve AppDbContext dependency. So if you don’t change these parts, Owin also needs to know how to create AppDbContext. But if you use true DI, Owin only needs to know about UserManager.

          • TheBigRic

            Thanx! I have some other questions concerning Owin (especially OwinContext) which is a little offtopic for this blogpost. Can I ask you this question somewhere else?

          • Sure, just replied to your @hotmail email.

    • Also see this answer that inspired second half of this post: http://stackoverflow.com/a/25556267/809357

  • Pingback: OwinContext and why you can have many of them « Trailmax Tech()

  • Gerard

    Hi,

    Thanks for this post. I’m trying to invalidate a user’s session in another browser when I change the user’s role and, quite frankly, I’m a bit at a loss. When I implement your solution in my app (from a standard MVC template) I get:

    The type ‘MyApp.ApplicationUserManager’ cannot be used as type parameter ‘TManager’ in the generic type or method ‘SecurityStampValidator.OnValidateIdentity(TimeSpan, Func<TManager, TUser, Task>, Func)’. There is no implicit reference conversion from ‘MyApp.ApplicationUserManager’ to ‘Microsoft.AspNet.Identity.UserManager’.

    And not only for my ApplicationUserManager but for my ApplicationUser too.

    My cookie config looks like:

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString(“/Account/Login”),
    Provider = new CookieAuthenticationProvider
    {
    // Enables the application to validate the security stamp when the user logs in.
    // This is a security feature which is used when you change a password or add an external login to your account.
    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity(
    validateInterval: TimeSpan.FromMinutes(30),
    regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
    getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))
    }
    });

    The template’s uses the OnValidateIdentity implementation with only 2 parameters that, too, has ApplicationUserManager and ApplicationUser as types without any problems. I’m using the template’s DI (never touched it):

    app.CreatePerOwinContext(ApplicationUserManager.Create);

    What am I missing? Or should I go about my session-problem entirely different?

  • Matt Roberts

    Cheers for this. I was wondering if you knew of a (IMO) major issue with the OnValidateIdentity stuff. Basically, if you set a low period for the validate interval (say, 1 minute), then wait for that perioid of time on your web app, and then try to log off, the validate identity code will trip over the log out code – so you get a cookie sign out immediately followed by a cookie re-issue from the OnValidateInterval code, which I’m sure is a reason for the many “why does my log out sometimes not work” questions on stack overflow.

    • I’ve never tried the scenario you are talking about – when SecurityStampValidator should re-create the cookie, but actually user asks to kill the cookie. I’ll check it out when I have a spare minute. That might be indeed an issue with OWIN/Identity.

    • Michael Peacockl

      Hi,
      I’m now getting an error in ClaimsIdentity GenerateUserIdentity(ApplicationUserManager manager) in the ApplicationUser:IdentityUser class.
      Error –‘ The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.’–

      My ExpirationTimeout is set to 1 and my Validation is set to 30sec. And I’m using a timed JS button.click to test the authentication function.

      I’m wondering if this could be a timing issue between the current cookie being deleted or even locked and the validation process. But I do not know how to diagnose. I certainly do not want Uses running into this issue in the real world.

      I’m new to this and would like to have any of your thoughts.

      • Most likely your problem is with your AplicationDbContext lifespan. DbContext is used by UserStore that is in turn used by ApplicationUserManager. And your exception says that DbContext has been disposed somewhere else before you had a chance to use it. Solution for that varies greatly, depending on your set up. First you can test this assumption by providing a new instance of ApplicationDbContext when you construct your ApplicationUserManager.