I have been updating my implementation of ASP.NET Identity all day today and I feel I'm on the last step, but just can't make it work. All I want to happen is to have the user's current session (if any) invalidated when something about them changes and to send them back to the login page. From the dozens of Identity related articles I've been reading today, I've settled that I have to override the OnValidateIdentity delegate, but it's just not working. Below is my code, I would really appreciate it if someone could tell me what I'm missing because I surely am not seeing it...
OwinConfiguration.cs
public static class OwinConfiguration {
public static void Configuration(
IAppBuilder app) {
if (app == null) {
return;
}
// SOLUTION: the line below is needed so that OWIN can
// instance the UserManager<User, short>
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<UserManager<User, short>>());
// SOLUTION: which is then used here to invalidate
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/"),
ExpireTimeSpan = new TimeSpan(24, 0, 0),
Provider = new CookieAuthenticationProvider {
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager<User, short>, User, short>(
// SOLUTION: make sure this is set to 0 or it will take
// however long you've set it to before the session is
// invalidated which to me seems like a major security
// hole. I've seen examples set it to 30 minutes, in
// which time a disgruntled employee (say, after being
// fired) has plenty of opportunity to do damage in the
// system simply because their session wasn't expired
// even though they were disabled...
validateInterval: TimeSpan.FromMinutes(0),
regenerateIdentityCallback: (m, u) => u.GenerateUserIdentityAsync(m),
getUserIdCallback: (id) => short.Parse(id.GetUserId())
)
},
SlidingExpiration = true
});
}
}
The GenerateUserIdentityAsync
method looked like it needed to be a part of the entity, which I didn't like, so I made an extesion method for it that's internal to the assembly with the OWIN configuration:
UserExtensions.cs
internal static class UserExtensions {
public static async Task<ClaimsIdentity> GenerateUserIdentityAsync(
this User user,
UserManager<User, short> manager) {
var userIdentity = await manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
I have a feeling that it has something to do with insancing the UserManager<User, short>
, but I can't seem to resolve it. I think the OWIN app has to create a singleton of it for the request, but it's not happening and thus the validation override isn't working? The thing is, I'm using Ninject, and I'm not sure how to make it cooperate with OWIN since OWIN is much earlier in the pipeline... Here's the Ninject configuration:
NinjectConfiguration.cs
namespace X.Dependencies {
using System;
using System.Linq;
using System.Web;
using Data;
using Data.Models;
using Identity;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Modules;
using Ninject.Web.Common;
using Services;
public static class NinjectConfiguration {
private static readonly Bootstrapper Bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start() {
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
Bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop() {
Bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel() {
var kernel = new StandardKernel();
try {
kernel.Bind<Func<IKernel>>().ToMethod(
c => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
} catch {
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(
IKernel kernel) {
if (kernel == null) {
return;
}
kernel.Bind<XContext>().ToSelf().InRequestScope();
kernel.Bind<IUserStore<User, short>>().To<UserStore>().InRequestScope();
kernel.Bind<IAuthenticationManager>().ToMethod(
c =>
HttpContext.Current.GetOwinContext().Authentication).InRequestScope();
RegisterModules(kernel);
}
private static void RegisterModules(
IKernel kernel) {
var modules = AssemblyHelper.GetTypesInheriting<NinjectModule>().Select(Activator.CreateInstance).Cast<NinjectModule>();
kernel.Load(modules);
}
}
}
A lot of the OWIN and Identity portions were put together by copy/pasting/adjusting from what I've found online... I would really appreciate some help. Thanks in advance!
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<UserManager<User, short>>());
. Second, I needed to change thevalidateInterval
toTimeSpan.FromMinutes(0)
so as to immediately check for invalidation. Before it was set to 30 minutes, so even though the user was invalidated as far as theSecurityStamp
, their session was not being expired. Now, it finally works, so I can finally move on to something more productive. Thanks for the help! – Barracuda