Build Your Own Membership System For ASP.NET MVC - Part II

Tuesday, October 2, 2012 by K. Scott Allen
9 comments

MemFlex is a look at what is possible in an ASP.NET MVC application if you eschew the existing ASP.NET providers for membership, roles, and OAuth. MemFlex doesn’t use the existing providers, but does use classes from the .NET framework and DotNetOpenAuth to build a membership and roles system with some simple requirements:

- Support all the actions of the MVC 4 Internet AccountController (register, login, login with OAuth).

- Be test friendly (by providing interface definitions for both clients and dependencies)

- Run without ASP.NET (for integration tests and database migrations, as two examples).

- Work with a variety of data sources (two common scenarios for storing use accounts these days involve document databases and web services).

Here are parts of the project as they exist today.

Sample Application

The sample application is a typical MVC 4 Internet application where the AccountController and database migrations use the FlexMembershipProvider. There are classes provided for working with both the Entity Framework and RavenDB, and you can (almost) switch between the two by adjusting an assembly reference and the namespaces you use.

After you’ve defined what a User model should look like, the first part would be configuring a FlexMembershipUserStore to work with your custom user type.

using FlexProviders.EF;

namespace LogMeIn.Models
{
public class UserStore : FlexMembershipUserStore<User>
{
public UserStore(MovieDb db) : base(db)
{

}
}
}

The above code snippet, which uses EF, just requires a generic type parameter (your user type), and a DbContext object to work with. The UserStore is then plugged into the FlexMembershipProvider. You can do this by hand, or let an IoC container take care of the work.

var membership = new FlexMembershipProvider(
new UserStore(context),
new AspnetEnvironment());

Once the FlexMembershipProvider is initialized inside a controller, you can use an API that looks a bit like the traditional ASP.NET Membership API.

_membershipProvider.Login(model.UserName, model.Password

Everything Else

The FlexProviders part of the project consists of 4 pieces: the integrations tests, a RavenDB user store, an EF user store, and the FlexProviders themselves.

FlexProviders

The FlexProviders project defines the basic abstractions for a flexible membership system. For example, the interface definition to work with locally registered users (for now, a separate interface provides the OAuth functionality):

public interface IFlexMembershipProvider
{
bool Login(string username, string password);
void Logout();
void CreateAccount(IFlexMembershipUser user);
bool HasLocalAccount(string username);
bool ChangePassword(string username, string oldPassword, string newPassword);
}

There is also a concrete implementation of a flexible membership provider:

public class FlexMembershipProvider : IFlexMembershipProvider, 
IFlexOAuthProvider,
IOpenAuthDataProvider
{
public FlexMembershipProvider(
IFlexUserStore userStore,
IApplicationEnvironment applicationEnvironment)
{
_userStore = userStore;
_applicationEnvironment = applicationEnvironment;
}

public bool Login(string username, string password)
{
var user = _userStore.GetUserByUsername(username);
if(user == null)
{
return false;
}

// ... omitted for brevity ...
}
// ...
}

Of course since the membership provider requires an IFlexUserStore dependency, the operations required for data access are defined in this project in the IFlexUserStore interface. There is also an AspnetEnvironment class that removes hard dependencies on test unfriendly bits like HttpContext.Current.

public class AspnetEnvironment : IApplicationEnvironment
{
public void IssueAuthTicket(string username, bool persist)
{
FormsAuthentication.SetAuthCookie(username,persist);
}

// ...
}

FlexProviders.EF and FlexProviders.Raven

It’s relatively straightforward to build classes that will take care of the data access required by a membership provider. For both Raven and EF, all you really need is a generic type parameter and a unit of work. For Raven:

namespace FlexProviders.Raven
{
public class FlexMembershipUserStore<TUser>
: IFlexUserStore where TUser : class, new()

{
private readonly IDocumentSession _session;

public FlexMembershipUserStore(IDocumentSession session)
{
_session = session;
}

public IFlexMembershipUser GetUserByUsername(string username)
{
return _session.Query<TUser>().SingleOrDefault(u => u.Username == username);
}

// ...
}
}

And the EF version:

namespace FlexProviders.EF
{
public class FlexMembershipUserStore<TUser>
: IFlexUserStore where TUser: class, IFlexMembershipUser, new()
{
private readonly DbContext _context;

public FlexMembershipUserStore (DbContext context)
{
_context = context;
}

public IFlexMembershipUser GetUserByUsername(string username)
{
return _context.Set<TUser>().SingleOrDefault(u => u.Username == username);
}

// ...
}
}

FlexProviders.Tests

This project is a set of integration tests to verify the EF and Raven providers actually put and retrieve data with real databases. The EF tests require the DTC to be running (net start msdtc). The tests are configured to use SQL 2012 LocalDB (for EF) by default, while the Raven tests use Raven’s in-memory embedded mode.

None of the code is vetted or hardened and still needs some work, but if you find it to be an inspiration or have some feedback or pull requests, let me know.

There are no NuGet packages available, as yet.

Conclusion

I said in the last post that building a membership system is easy if you know exactly what you want. You can mostly rely on other pieces of the framework for the hard parts (creating secure cookies and cryptography, for example, but also relying on DotNetOpenAuth for the OAuth heavy lifting).

The hard part of building a membership system is when you try to build it for unknown customers and unknown scenarios. I don’t envy Microsoft in the sense that if they build a membership system that is simple, 60% of their customers will say it doesn’t work for their application. If they build a membership system that is sophisticated enough to work with most applications, 60% of their customers will say it looks like WCF. Somewhere there is a sweet spot that will make a majority of customers happy.

What I have here will work for most of the applications I’ve been associated with, provides some flexibility in the data store, and still remains, I think, relatively easy to understand.

Build Your Own Membership System For ASP.NET MVC - Part I

Monday, October 1, 2012 by K. Scott Allen
10 comments

Membership Provider Base ClassBuilding a piece of software to manage users is easy, but only if you know exactly what you want. After all, most of the code inside the various existing ASP.NET providers consists of straightforward parameter validation and data access. While this membership code is simple in isolation, there is still value inside the existing providers. The providers have proven themselves in production for thousands of web sites.

Unfortunately, it is difficult to derive value from the existing providers and reuse just the parts you need when building a custom membership solution for an application. The providers entangle a number of responsibilities and require a relational database. This has always been a source of frustration when building YACMP (yet another custom membership provider). My typical approach is to start from scratch by deriving from the abstract MembershipProvider class.

However, starting with the abstract MembershipProvider class doesn’t give me any inherent benefits in an ASP.NET MVC or Web API application. There are no custom controls to drag from the toolbox that will automatically integrate with a custom provider, and other than the Authorize attribute (which works against the roles provider), there is no implicit dependency on Membership.Provider or Roles.Provider, which are the typical static gateways to membership and role features.

There are actually drawbacks to building  custom providers with ASP.NET MVC. The provider model doesn’t easily cooperate with the dependency resolution features of MVC and Web API. Also, the API is a bit dated and doesn’t have the ability to work with OAuth or OpenID.

The solution to the OAuth problem in a new MVC 4 Internet application is to combine a new membership provider (the SimpleMembershipProvider) with some Web Matrix bits (the WebSecurity class) into something that works with OAuth and still allows a user to register locally with a password, but unfortunately still depends on a relational database and is complicated to understand, extend, and debug (search for MVC 4 SimpleMembership and you’ll find more questions on StackOverflow than anything else).

Given that the traditional provider model doesn’t provide many benefits for MVC and WebAPI, what would it look like to build a membership system and not start by deriving from MembershipProvider? That’s the topic for the next post.

by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!