Simple cookie based Owin authentication

Summary

I feel like I was one of the few people that thought SimpleMembershipProvider was an OK extension of the old-timey membership provider. With minimal effort I could create custom accounts, recover accounts via tokenized links, login, add roles, and more. It does have its flaws; one of which is that it isn't customizable making it difficult to evolve with a project.

Owin saves the day

Owin is a nicely decoupled membership middleware that is completely customizable and lacks all of the leakiness of the old MembershipProvider. For the implementation in this post we will focus on signing in with cookie persistence and maybe tackle user creation later.

Getting the necessary packages

To get started we will need to install the following packages with nuget:

Install-Package Microsoft.Owin
Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package microsoft.AspNet.Identity.Core

Initialization

Owin was designed to be decoupled from web dependencies, because of this we need a way to hook into the application initialization outside of Global.asax. Owin by itself has no way to initialize configuration which is why we installed the Microsoft.Owin.Host.SystemWeb package which provides a couple options for start up.

Microsoft.Owin.Host.SystemWeb looks for a class with a void Configuration(IAppBuilder app) signature. To call this method we need to make known the namespace and containing class. This can be done via a web.config app setting, naming convention (Startup.cs), or with a namespace decorated with the OwinStartupAttribute.

The OwinStartupAttribute is my prefered way of initializing Owin since I can create App_Start/AuthConfig.cs and follow the startup convention already in use by the Global.asax (routes, filters, etc), and I don't have to deal with the web.config at all.

After installing the necessary packages above, create App_Start/AuthConfig.cs, and place the following code inside to tell Owin to UseCookieAuthentication:

using System;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
                
[assembly: OwinStartupAttribute(typeof(SimpleOwin.AuthConfig))]
namespace SimpleOwin
{
    public class AuthConfig
    {
        public void Configuration(IAppBuilder app)
        {
            var cookieAuthOptions = new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                CookieHttpOnly = true,
                ExpireTimeSpan = TimeSpan.FromMinutes(60),
                SlidingExpiration = true,
                CookieSecure = CookieSecureOption.SameAsRequest,
                LoginPath = new PathString("/Account/Login")
            };
            app.UseCookieAuthentication(cookieAuthOptions);
        }
    }
}

Now on application start you can throw a breakpoint on public void Configuration(IAppBuilder app) and you should be able to step through it.

IUser

I find it easier to keep track of what is going on in a solution by keeping my layers fairly focused. In this solution my Core project generally houses all business logic which includes the Owin UserManager, IUserStore, and IUser.

IUser in the most basic form keys the implemented class off of the UserName; I don't like this, and IUser offers up a generic key which I generally set as long or int on an Id property.

using Microsoft.AspNet.Identity;
            
namespace SimpleOwin.Core.Models
{
    public class User : IUser <long>
    {
        public long Id { get; set; }
        public string UserName { get; set; }
        public string FirstName { get; set; }
    }
}

IUserStore

Next step is to create the UserStore used for data access operations such as user creation, finding users, adding roles, etc. For this example the UserStore isn't providing us any functionality since all we are doing is authenticating. I've filled out one method as an example, since Owin's UserStore cannot be initalized without an instance of an IUserStore.

using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using SimpleOwin.Core.Models;
using SimpleOwin.Core.Services;
            
namespace SimpleOwin.Core.Security
{
    public class SimpleUserStore<T> : IUserStore <T, long> where T : User
    {
        private readonly IUserService _userService;
            
        public SimpleUserStore(IUserService userService)
        {
            _userService = userService;
        }
            
        public Task CreateAsync(T user)
        {
            throw new NotImplementedException();
        }
            
        public Task DeleteAsync(T user)
        {
            throw new NotImplementedException();
        }
            
        public Task<T> FindByIdAsync(long userId)
        {
            throw new NotImplementedException();
        }
            
        public Task<T> FindByNameAsync(string userName)
        {
            var task = Task<T>.Run(() =>
            {
                return (T)_userService.GetUserByUsername(userName);
            });
            
            return task;
        }
            
        public Task UpdateAsync(T user)
        {
            throw new NotImplementedException();
        }
            
        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }
}

Using dependency injection like a good little class the SimpleUserStore expects an IUserService to provide it access to my business logic which in turn will make calls to a UserRepository. You could pass an IUserRepository instead of a service to SimpleUserStore, but I like to hide the data layer completely from my web project and keeping things consistent by going through a service that calls my data access layer.

UserManager

Working our way back up the dependency tree, getting closer to the controller we implement a UserManager which is responsible in this scenario for authentication, creating an identity, and logging a user out.

using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using SimpleOwin.Core.Models;
using SimpleOwin.Core.Services;
            
namespace SimpleOwin.Core.Security
{
    public interface ISimpleUserManager
    {
        Task<User> FindAsync(string userName, string password);
        Task SignInAsync(User user, bool isPersistent);
        void SignOut();
    }
            
    public class SimpleUserManager : UserManager<User, long>, ISimpleUserManager
    {
        private readonly IUserService _userService;
        private readonly IAuthenticationManager _authenticationManager;
            
        public SimpleUserManager(IUserService userService, IAuthenticationManager authenticationManager)
            : base(new SimpleUserStore<User>(userService))
        {
            _userService = userService;
            _authenticationManager = authenticationManager;
        }
            
        public override Task<User> FindAsync(string userName, string password)
        {
            var task = Task<User>.Run(() =>
            {
                var user = _userService.Authenticate(userName, password);
            
                return user;
            });
            
            return task;
        }
            
        public async Task SignInAsync(User user, bool isPersistent)
        {
            SignOut();
            
            var identity = await base.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
            _authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
        }
            
        public void SignOut()
        {
            _authenticationManager.SignOut();
        }
    }
}

I love the fact that every method in the UserManager and IUserStore returns a Task. It really is the icing on the cake for me. While the IUserStore is out finding or updating user information the rest of the code base continues on simultaneously working and meeting up again with await to return a result.

The Login action

Normally the AccountController would be injected with IServices, but for sake of simplicity in this example the parameterless constructor mocks my IUserService to pass to the SimpleUserManager which would also normally be injected.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model)
{
    if (!ModelState.IsValid)
        return View(model);
            
    var manager = new SimpleUserManager(_userService, _authManager);
    var user = await manager.FindAsync(model.UserName, model.Password);
            
    if (user != null)
    {
        await manager.SignInAsync(user, isPersistent: false);
            
        return RedirectToAction("Index", "Home");
    }
            
    return View(model);
}

If login succeeds the user will be redirected to the home page, and the basic building blocks are set in place to start implementing account creation, recovery, roles, and more.

Conclusion

Moving away from SimpleMembershipProvider was an easy decision; while being extremely easy to implement it's biggest faults were being built on a leaky foundation and violating SOLID principles.

Owin is such a welcome upgrade, and so far has been a pleasure to work with. Getting the most primitive membership pipeline working takes relatively little effort, it is completely open for extension and customization. Most importantly it provides a strong foundation for future needs.