ASP.NET Core Identity

Monday, November 25, 2013

ASP.NET Core Identity AbstractionsThis release of Visual Studio 2013 and ASP.NET features YAMF - yet another membership framework. The new features build on top of an assembly and NuGet package named Microsoft.AspNet.Identity.Core.

The core identity abstractions are interface based and revolve around the concepts of users and stores. Users are the people who are happy to authenticate themselves to your web application, while stores are the components happy to persist and retrieve user information from a data source. 

These abstractions are more flexible and composable than the membership providers from previous releases of ASP.NET and don’t rely on the “membership provider” base classes that have been around since ASP.NET 2.0. 

In the next few blog posts we’ll look at how the components work, how to customize the identity features, how to work with 3rd party claims, and how to build a custom store using a non-relational database (specifically MongoDB).

This first post in the series looks at the core abstractions. It’s important to note that the Identity Core assembly defines mostly abstractions with no implementations. A later post will look at components provided by Microsoft to implement these interfaces and provide membership features backed by SQL Server.

IUser

A user object must implement the IUser interface, which requires every user to have at least an ID and a name. The most notable aspect of IUser is the Id property of type string. Strings work well for GUIDs but can be a bit tricky when using integers for keys as is common with SQL Server. More details in a future post.

IUserStore

The I*Store interfaces in the Core Identity assembly are factored to offer various levels of functionality.

IUserStore defines the bare minimum functionality you’d want for users – create, retrieve, update, and delete (CRUD) functionality. If your website wants to allow users to create local accounts with passwords, you’ll want a component implementing IUserPasswordStore (which is an IUserStore). For third party logins (Twitter and Facebook, for example), add in IUserLoginStore, and for claim storage there is an IUserClaimStore. Also, not shown in the class diagram above, is a collection of interfaces for roles (IRole, IUserRoleStore, etc). Let the debate on roles versus claims begin.

One of the curious aspects of IUserStore is its generic constraint.

public interface IUserStore<TUser> : IDisposable 
   where TUser : IUser
{
  // ...
}

The implication is that any user object you want to use with the Identity features will need to implement IUser, which requires a dependency on the core identity assembly. It’s a bit odd to place a constraint on an interface definition since an interface doesn’t provide an implementation, but presumably the constraint helps to enforce the concept of all users having username and IDs of type string. The method signatures for IUserStore make this assumption, as does the UserManager.

ASP.NET UserManagerUserManager

The UserManager is a concrete class and it is the UserManager that provides the domain logic for working with user information. The UserManager knows when to hash a password, when to validate a user, and how to manage claims.

There are a few extensibility points with the UserManager, for example, the manager has a number of properties you can use to set a custom user validator (any object implementing IIdentityValidator), a custom password validator, and a custom password hasher. The primary extensibility point is via the UserManager constructor, which allows you to pass in any object implementing IUserStore. If you want to use a UserManager to manage user information stored in SQL Server, we’ll look at pre-built classes to do this in a later post, but it is also easy to create an IUserStore to work with a document database or other forms of storage.

Remember an IUserStore doesn’t know how how to work with user passwords, only an IUserPasswordStore knows about passwords. The UserManager is aware of the different core interfaces and will try to work with the capabilities of the store object given in the constructor. For example, if you use FindByIdAsync the UserManager can use the user store to query for a user by ID, but if you invoke FindAsync (which takes a username and a password), and the underlying store doesn’t implement the IUserPassword store interface, the UserManager will be forced to throw an exception.

It’s tricky to use a single concrete class for everything from users to passwords to claims, and tradeoffs must be made. We’ll also look at some of the tradeoffs in upcoming posts.

Async

Something you might have noticed in the class diagrams is that every nearly every method on the store and manager classes is an async method. The return types for these methods are all Task<T>, where the Task represents the outstanding work of querying or updating user information.

These async methods work well with the Entity Framework version 6, which now includes an async API. We’ll also look at implementing these methods using a non-async data source in a future post.

Conclusions

The new Identity features are a welcome break from the dated model of the ASP.NET membership providers and more flexible than the the SimpleMembershipProvider in the last ASP.NET release.

Some developers will notice a lack of features compared to the membership providers of old (like the ability to track the last login and bad password attempts). Some of these features are easy to add, but it will be interesting to see the challenges that result if Microsoft attempts to add these features to the Identity bits in a future release (expect more interface definitions to appear).

Something I learned when building Memflex is that a membership framework has to make some assumptions and build itself around some core constraints which can limit the usefulness of a membership component in some applications – the needs for membership are just so varied in this big world. Still, the Identity bits have done a good job of abstracting away persistence concerns from the logic of managing users, logins, clams, and passwords, and as we’ll see in a future post (lots of future promises in this post), the implementation of the persistence APIs is mostly formulaic.


Comments
gravatar Bret Ferrier @runxc1 Monday, November 25, 2013
So is the new asp.net core identity OWIN compatible in that it takes no reliance on System.Web?
gravatar Craig Berntson Monday, November 25, 2013
I hope you cover some things that are lacking in the official MS docs: - If you're using x type auth in the old system, what does that map to in the new system? - I've seen confusion from people around "Windows" auth, particularly when trying to map to AD groups using the AutenticateAttribute on a controller - When selecting one of the auth types that requires AD, what is that AD string supposed to look like in the dialog
gravatar Khalid Abuhakmeh Monday, November 25, 2013
You don't actually need to utilize the Entity Framework if you don't have to. I wrote a blog post about the basic necessities needed to use the new OWIN auth without jumping into Entity Framework or avoiding the IUserManager interface. http://www.khalidabuhakmeh.com/asp-net-mvc-5-authentication-breakdown-part-deux
gravatar Scott Allen Monday, November 25, 2013
@Bret: Yes, that's right. @Craig: Yes, I can post about that. @Khalid: I never indicated that you have to use EF, I even foreshadowed a document DB implementation.
gravatar Eugene Tuesday, November 26, 2013
Great introductory post. Looking forward to an article about how to make it work with an existing database where user IDs are stored as integers.
gravatar Michael Reyeros Tuesday, December 10, 2013
Great article introducing the new Identity framework! When do you think that you will have the next article, specifically covering how to change the IUser Id property from a string to an int and implement that across, perhaps using EntityFramework?
gravatar Scott Thursday, December 12, 2013
@Michael: Hopefully in the next 2 weeks.
gravatar Eren Tatar Wednesday, January 1, 2014
Hi, this is really a helpful post for understanding the underlying concept at ASP.NET Identity. Now, I am looking forward to see future posts, especially interested in how to work with roles or claims and implementing those methods as non-async.
gravatar Murali M Wednesday, January 1, 2014
Thanks for the Excellent initial simple detailed post. It helped me to quickly understand how IUser and other interfaces built. Eagerly waiting for your next article on this as I am trying to implement ASP.Net identity with out EF with my old DAL and POCO classes. Our ApplicationUser, Role, RoleXFeature in a database got a different schema with ID as a INT :)
gravatar john pauk Wednesday, January 8, 2014
Great blog post mate! I do have a question about dependencies. Is it bad practice or acceptable for my hexagonal architectures project to refence and use aspnet.identity.core from within my domain model? What are your thoughts on this and recommendation?
gravatar scott Wednesday, January 8, 2014
@john: In general I think it is a bad idea. It is unfortunate that IdentityUser is in an assembly that has an EF dependency. The only way around this today is to use only IUser, but then you need to implement your own UserStore (which isn't really too difficult, especially if you don't need claims or 3rd party logins, but even then it is mostly basic CRUD operations).
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!