ASP.NET Identity with the Entity Framework

Friday, January 3, 2014

ASP.NET EF Identity In a previous post  (Core Identity), we saw how the .Core identity assembly provides interfaces for describing the data access needs of a membership and identity system. Core also provides a UserManager class with the domain logic for identity management.

The .EntityFramework identity assembly provides concrete implementations for the core interfaces.

Here are 5 things to know about how it all works together.

In A New MVC 5 Application

If you use File –> New Project to create an MVC 5 application with the “Individual User Accounts” security option, the new project template will spit out all the code needed for users to register, login, and logoff, with all information stored into a SQL Server database.

The new identity bits do not support some of the features included with membership providers in the years past, features like counting invalid login attempts and lockouts, but the extensibility is in place and the current implementation has some clean separations, so perhaps they’ll be in by default in the future.

Remember the UserManager is the domain logic, and the UserManager needs (at a minimum) an IUserPasswordStore to persist users and passwords. Thus, the way the default AccountController constructs a UserManager is by passing in a new UserStore, which implements IUserPasswordStore in addition to the other core identity interfaces for persisting claims, roles, and 3rd party logins.

new UserManager<ApplicationUser>(
    new UserStore<ApplicationUser>(
        new ApplicationDbContext()))

It turns out that UserStore also has a dependency, a dependency on an EF DbContext class, and not just any context but one that derives from IdentityDbContext. IdentityDbContext provides all of the EF code-first mapping and DbSet properties needed to manage the identity tables in SQL Server. The default “new project” code provides an ApplicationDbContext that derives from IdentityDbContext with the idea that you’ll add your own DbSet properties for the entities, tables, and overall data that your application needs and keep everything in the same database.

In short, an identity specific DbContext plugs into the concrete user store, which then plugs into the user manager.

Once all three are together you have an identity system that supports third party logins as well as local accounts.

In A New Web API or SPA Application

The WebAPI and Single Page Application project templates also support user registration and password logins, but in these templates the AccountController is an API controller that issues authentication tokens instead of authentication cookies. Because the identity management work happens inside both the AccountController and inside Katana middleware, a UserManager factory is responsible for creating the user manager that both the middleware and API controller share.

public static Func<UserManager<IdentityUser>> UserManagerFactory { get; set; }

This static property is in the Startup.Auth.cs file that holds the Katana Startup configuration class. The actual factory function is initialized in this class, also.

UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>());

This code uses the default constructor for the UserStore class, which will create a new instance of an IdentityDbContext object since an object isn’t supplied. If you want to use your own IdentityDbContext derived class, like the MVC 5 project does, you can modify the above initialization code and pass in your own context.

Side note: the SPA application template produces more than 2700 lines of code to get started. Not a large amount, but there are structural design issues (like the static UserManagerFactory) that require a healthy amount of rework for real applications. My personal advice is to use the template to get some ideas, but throw the code away and start from scratch for production applications.

Connection Strings

By default, IdentityDbContext uses a connection string named “DefaultConnection”, and all the new project templates will include a DefaultConnection connection string in the project’s web.config file. The connection string points to a SQL Local DB database in the AppData folder.

To change the SQL Server database being used, you can change the value of the connection string in web.config. You can also pass a different connection string name into the DB context constructor.

The Easy Customizations

A new MVC 5 project (not the SPA or WebAPI templates) provides an IdentityModels.cs file with the following two classes.

public class ApplicationUser : IdentityUser
{
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {

    }
}

Remember ApplicationDbContext is the context used to initialize a UserStore for a UserManager. The context will already include Users and Roles properties it inherits from IdentityDbContext, but you can add additional properties to store movies, books, accounts, employees, or whatever an application needs to solve a problem.

The ApplicationUser class includes Id, Username, PasswordHash, and other properties it inherits from IdentityUser. You can add additional properties here to store additional profile information about a user.

For simple applications this all works well, but for more complex applications you again probably want to use the starting project template code only for inspiration and start your own implementation from scratch. The names, structure, and code organization all have a prototype code feel and aren’t production ready.

Managing Contexts

In a real application you’ll have to decide if you want to mingle your data context with IdentityDbContext. One issue to be aware of is that the UserStore class does not play well when using the unit of work design pattern. Specifically, the UserStore invokes SaveChanges in nearly every method call by default, which makes it easy to prematurely commit a unit of work. To change this behavior, change the AutoSaveChanges flag on the UserStore

var store = new UserStore<ApplicationUser>(new ApplicationDbContext());
store.AutoSaveChanges = false;

What’s Next

The new Identity system is easy for greenfield projects with no existing users, but quite a bit trickier if you have an existing schema or don’t use the Entity Framework. Fortunately, the separation of domain logic in the UserManager and persistence logic behind IUserStore and friends is a fairly clean separation, so it is relatively easy to implement a custom persistence layer. This is one of the topics we’ll cover in a future post.


Comments
gravatar Khalid Abuhakmeh Friday, January 3, 2014
My personal opinion is that Microsoft did waaaaaaaay too much with the the implementation of the userstore. I count 22 methods on a single interface. The issue is that Microsoft is mixing concerns here that are partly infrastructure and partly business logic. Claims, Cookies, and Security (hashing of passwords, and verification) are infrastructure and are good to standardize, just give the classes (not connected to any backing storage). Who your users are, how you identify them and search for them, and their roles is a business logic concern (which imho would be better implemented by the user). Examples of business logic rules that feel smelly in the model here: User has one role and only one role. User's are identified by email, User is part of a company (claim maybe, but ewwww), User can have multiple accounts, etc... These are all business rules that eventually will make user's frustrated because this is being sold as THE MICROSOFT solution and it will not mesh with what they might be thinking of conceptually. I think I'd rather just have a nice Readme that comes with every ASP.NET MVC application that explains in simple steps how to use the auth stuff and how security works in asp.net (sans Entity Framework or the IUserStore interface). Then a few simple scenarios to implement your own logic and data access if you need to do so. All that being said, I think you wrote a good post explaining how to use the new identity stuff in ASP.NET and I enjoyed reading it. Thanks :) P.S. UserStore is a bad abstraction in my opinion because if you are using a document database, you'll end up querying and saving the same user document multiple times to fulfill the interface contract and you will probably lose the ability to optimize without jumping through odd hoops.
gravatar Jon Friday, January 3, 2014
Thanks for this post. When you mention "Creating your own implementation from scratch", how would one approach this? I would generally just extend the ApplicationUser : IdentityUser class with the objects that I would like to map to using FluentAPI. Is this not the best practice?
gravatar Scott Friday, January 3, 2014
@Khalid: The UserManager does work with what you give it in terms of interfaces. So, if you implement only an IUserStore (6 methods), you can store usernames (but not passwords or claims or logins). I think it is impossible to do a one size fits all identity system which is why nearly everyone gets frustrated with membership systems at some point.
gravatar Khalid Abuhakmeh Friday, January 3, 2014
@Jon creating your own implementation is super easy especially in ASP.NET MVC 5. You just need to translate your User (domain object) into a ClaimsIdentity. Once you have that Identity, you can use the IAuthenticationManager to create an ApplicationCookie. Here I show you how to do it, without any data storage mechanism. http://www.khalidabuhakmeh.com/asp-net-mvc-5-authentication-breakdown-part-deux. Once you have that, you can go nuts with your code or keep it simple (whatever you like).
gravatar Scott Friday, January 3, 2014
@Jon: I think that is fine if it works for you. People who want to start from scratch are usually looking to do something like create a .Domain or .Core assembly with their own definition of a user that can plug into the Identity framework. Unfortunately, this means you either take a dependency on the Entity Framework in a domain assembly (to inherit from IdentityUser) or re implement IUser and IUserStore from scratch. It's also much easier to use classes like ApplicationUser for a new application but a bit difficult to manage with an existing schema until you've figured out all the pieces (like how to do mapping with EF).
gravatar Mathieu Friday, January 3, 2014
This is a wonderful article about asp.net identity, it mademoiselle things much clearer for me. Thanks! To go even deeper, i suggest you to explain how to implement a userstore for azure table... It would explain by code how to implement identity from scratch. Just à suggestion... Mat
gravatar Eren Tatar Friday, January 3, 2014
When I start a new project, first of all I design and create DB then by using reverse engineering (with EF) I create my domain objects (entity layer). In this case, I will not have Asp.Net Identity db tables. By using code-first approach I want to add them,too before I start to write the data access layer. But when and where should I incorporate Asp.Net Identity into my project ? Since it is related with authentication and authorization, it should not be in entity layer or UI layer. Most probably, it should be in a core common, or a cross cutting concerns layer, but I do not know how to create tables in db by using code-first approach.
gravatar scott Saturday, January 4, 2014
@Eren: What might be the easiest approach is to just start a new application, run it and register a user (to force the application to create the database), and then go in and generate some scripts from those tables to add into your own database schema. Either that, or use the EF migrations feature (enable-migrations) and the command update-database -script will generate a script for you.
gravatar Brian Monday, January 6, 2014
I like the new EF based Identity Framework, But what I *hate* is the lack of separation of concerns. I try to organize my application into UI, Domain, Business, and Repository assemblies. Where my UI should not have any database logic. Repository has all database access logic. Business has business rules and domain contain DTO and other data models. This completely breaks this pattern. I have to have EF referenced in all of my assemblies to inherit from the ApplicationUser object. The Implementation doesn't make it easy to abstract the data access out of the UI. I'm forced to put business logic in my controller instead of my business classes. The UI is doing direct database access via EF.
gravatar Scott Monday, January 6, 2014
@Brian - right, that's the problem I pointed out to @Jon (taking a dependency on IdentityUser requires EF). The only way around is to take a dependency on IUser and the Core assembly instead, but then implement your own user store, which in the end you might as well start the whole thing from scratch.
gravatar james Monday, January 6, 2014
If you're looking for an alternative to ASP.NET Identity, checkout Brock Allen's MembershipReboot. https://github.com/brockallen/BrockAllen.MembershipReboot Open source (unlike ASP.NET Identity), works with EF, nHibernate and NoSQL.
gravatar Konstantin Tarkus Monday, January 6, 2014
Those of you, using Database-First, check out this ASP.NET Identity Database project template for Visual Studio: http://visualstudiogallery.msdn.microsoft.com/6780f8e4-d204-4e88-83c2-853098727ffb
gravatar Anon Tuesday, January 7, 2014
You people are never happy.
gravatar Lajos Marton Wednesday, January 8, 2014
My problem with this new ASP.NET Identity, that it completly breaks Repository pattern, which is I think the most used pattern in ASP.NET MVC. For Example ApplicationUser : IdentityUser is a complex type, so it is useless for Base Repositories. And what are these stupid GUID Ids? And this is the out-of-the-box Microsoft new solution?
gravatar Sean Sunday, February 2, 2014
So is MVC going to die with everyone jumping on the Angular train and because of Node a la the MEAN stack? ....or am I letting the blogosphere get to me too quickly? :)
gravatar Scott Sunday, February 2, 2014
@Sean. I think there is plenty of room for both. I have been using Node for quite a few things lately, though, but I still have MVC and even ebForms applications around.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!