Simple user authentication: ASP.NET Identity and NHibernate

By: on July 7, 2016

ASP.NET MVC 5 introduced a new system for managing users and authentication, known as ASP.NET Identity. Over the last few years we have built and now maintain a couple of MVC applications. We started with MVC 2, and have since upgraded to 3 and then 4. So despite MVC 5 being out since the end of 2014, it’s only with the start of a new project (currently hush hush!) that I have had a chance to properly get stuck into MVC 5.

ASP.NET Identity is at the “general solution” end of programming approaches. It’s designed to be extensible, and integrate with all kinds of methods of authentication (OAuth, Facebook, Twitter, LinkedIn, Microsoft Account…) all kinds of storage layers, it supports two-factor auth and a whole bunch of other features. Whether or not these are done well is a question for another day.

The problem we had was that we needed to quickly put together a prototype to demonstrate to a client the look and feel of a proposed website. This meant we needed just a basic user authentication system, backed by NHibernate, our ORM layer. Previously we would have used SimpleMembership, which is now obsolete. I was not able to find much documentation about how to do this – most guides assume Entity Framework. So I have put together a minimal working example of how to do basic user management with MVC 5 & NHibernate, which you can see here.

Before I continue, please note that this code is just a simple worked example, destined for a prototype. Please think through your own security requirements carefully.

Preamble done, here’s the answer you were Googling for: In order to get simple user auth working with ASP.NET identity with NHibernate as your storage mechanism you need to:

  1. Create a user class and map it with NHibernate
  2. Create a class that implements IUserStore
  3. Create ASP.NET Identity manager classes and configure OWIN to use them
  4. Put together a simple controller, view models, and views to provide login, register and log out features

1. Create a user class and map it with NHibernate

Here, I’m using Fluent NHibernate for the mapping, but you could map the User class any other way that NHibernate supports. The only ASP.NET Identity bit here is the fact we implement IUser. The int type argument refers to the fact that Id is an integer; you could use something else.

public class User : IUser<int>
{
    public virtual int Id { get; protected set; }
    public virtual string UserName { get; set; }
    public virtual string PasswordHash { get; set; }

    public class Map : ClassMap<User>
    {
        public Map()
        {
            Id(x => x.Id).GeneratedBy.Identity();
            Map(x => x.UserName).Not.Nullable();
            Map(x => x.PasswordHash).Not.Nullable();
        }
    }
}

2. Create a class that implements IUserStore

This is the only part with any real meat. IUserStore defines how new Users are saved to the database, how they are retrieved (by Id or by UserName), and how they are updated and deleted. The ASP.NET Identity code will actually cast this class to other interfaces, so although we won’t get a compile time warning, we also need to implement IUserPasswordStore (describing how password hashes get retrieved and set) and, sadly, two useless interfaces. These are IUserLockoutStore and IUserTwoFactorStore, and even if you disable, or just aren’t using, user lockout and two-factor auth ASP.NET Identity will still try and cast our class, so we need stub implementations.

public class IdentityStore : IUserStore<User, int>, IUserPasswordStore<User, int>,
    IUserLockoutStore<User, int>, IUserTwoFactorStore<User, int>
{
    private readonly ISession session;

    public IdentityStore(ISession session)
    {
        this.session = session;
    }

    public Task CreateAsync(User user)
    {
        return Task.Run(() => session.SaveOrUpdate(user));
    }

    public Task<User> FindByIdAsync(int userId)
    {
        return Task.Run(() => session.Get<User>(userId));
    }

    public Task SetPasswordHashAsync(User user, string passwordHash)
    {
        return Task.Run(() => user.PasswordHash = passwordHash);
    }
...

I won’t include the full class here, because it’s quite long, but note that we pass in an NHibernate session. See the full class here.

3. Create ASP.NET Identity manager classes and configure OWIN to use them

We need a UserManager. In the constructor we could configure all kinds of things: Two-factor auth, how passwords are hashed, password requirements, and so on. For now let’s keep it simple. Note that when we construct UserManager we are going to give it our IUserStore.

public class UserManager : UserManager<User, int>
{
    public UserManager(IUserStore<User, int> store)
        : base(store)
    {
        UserValidator = new UserValidator<User, int>(this);
        PasswordValidator = new PasswordValidator() { RequiredLength = 6 };
    }
}

We also need a SignInManager. There is less to configure here; I’ve just extended the base method with a convenience SignOut method.

public class SignInManager : SignInManager<User, int>
{
    public SignInManager(UserManager<User, int> userManager, IAuthenticationManager authenticationManager)
        : base(userManager, authenticationManager) { }

    public void SignOut()
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
    }
}

The real magic happens when we tell OWIN to use these classes. All OWIN applications have a startup class with one of those horrible implicit interfaces that ASP.NET is so keen on. You can specify which class is the startup class using an assembly attribute: [assembly: OwinStartup(typeof(Startup))] and then the class looks like this:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.CreatePerOwinContext(() => new UserManager(new DatabaseContext().Users));
        app.CreatePerOwinContext<SignInManager>((options, context) => new SignInManager(context.GetUserManager<UserManager>(), context.Authentication));

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider()
        });
    }
}

This tells OWIN to, when it wants a UserManager, to construct it like this and when it wants a SignInMananager, make it like that. DatabaseContext is a simple class that handles the connection string to the database and provides an IdentityStore (our implementation of IUserStore) wrapping an ISession. We also tell Owin that we want to use cookie based authentication, and what the login URL is.

4. Put together a simple controller, view models, and views to provide login, register and log out features

This is the straightforward part you have probably done before. You need a view model for logging in that takes a user name and password, you need a view to render the login form, and you need a controller action that ties those things together. The only ASP.NET Identity specific part is the post-back, where we use the SignInManager:

[HttpPost]
public ActionResult Login(LoginViewModel model)
{
    if (ModelState.IsValid)
    {
        var result = SignInManager.PasswordSignIn(model.UserName, model.Password, false, false);
        if (result == SignInStatus.Success)
        {
            return RedirectToAction("Index", "Home");
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }
    return View(model);
}

public SignInManager SignInManager
{
    get { return HttpContext.GetOwinContext().Get<SignInManager>(); }
}

For register, the important logic is:

var userManager = HttpContext.GetOwinContext().GetUserManager<UserManager>();
var user = new User() { UserName = model.UserName };
var result = userManager.Create(user, model.Password);

and for log off it’s just SignInManager.SignOut().

It’s work noting that a number of the methods in the controller methods are extension methods to earlier ASP.NET MVC classes, and you need the right libraries referenced and using statements to get them in scope. Please see the full example project to see which NuGet packages you need.

That’s all you need for basic user authentication with ASP.NET Identity and NHibernate. It should give you a good starting point for exploring the features of ASP.NET Identity.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*