Quantcast
Channel: ASP.NET Blog
Viewing all articles
Browse latest Browse all 7144

Per request lifetime management for UserManager class in ASP.NET Identity

$
0
0

Introduction

We recently released the 2.0.0-beta1 version of ASP.NET Identity. Learn more here by visiting this link. This is an update to 2.0.0-alpha1 and adds the two-factor auth feature along with a few bug fixes. To learn more about the Alpha release, please visit this link.

In this article I am going to explain the significance of the UserManager class in an application and some of the best practices when using it. I am going to use an MVC application that is created using VS 2013 RTM. In the Visual Studio 2013 RTM templates, which had the 1.0.0 version of ASP.NET Identity, we demonstrated directly instantiating the UserManager class as needed in the application. This approach had a few issues, which are explained further in this article. These have been fixed in 2.0.0-beta1.

Understanding Managers and Stores

ASP.NET Identity consists of classes called managers and stores. Managers are high-level classes which an application developer uses to perform operations in the ASP.NET Identity system, such as creating a user. Stores are lower-level classes that specify how entities, such as users and roles, are persisted. Stores are closely coupled with the persistence mechanism, but managers are decoupled from stores which means you can replace the persistence mechanism without disrupting the entire application.

The current article outlines the steps to configure the UserManager in the application. We will start with an application with Identity 1.0.0. We will migrate the solution to 2.0.0-beta1, and change the way UserManager is instantiated and used in the application. Additionally, with this approach it is possible to configure the properties on the UserManager, such as password length and complexity.

Create Application

In Visual Studio 2013 RTM create a new web application. Choose MVC

clip_image002

UserManager explained

Let us briefly look at how the UserManager class is used in the application. All user account management actions are defined in the AccountController class.

Code Snippet
  1. public AccountController()
  2.             : this(newUserManager<ApplicationUser>(newUserStore<ApplicationUser>(newApplicationDbContext())))
  3.         {
  4.         }
  5.  
  6.         public AccountController(UserManager<ApplicationUser> userManager)
  7.         {
  8.             UserManager = userManager;
  9.         }
  10.  
  11.         publicUserManager<ApplicationUser> UserManager { get; privateset; }

The controller has a UserManager property of type UserManager which is set in the constructor. The UserManager class takes in an instance of UserStore class which implements operations on the user that are persistence-specific. In our case, we have a UserStore class which implements these operations specific to EntityFramework. To persist data to/from the database the UserStore class takes in an instance of DBContext class. The UserStore class is defined in the Microsoft.AspNet.Identity.EntityFramework assembly.

Note: The rationale behind implementing this pattern is that if the developer wishes to store user information in the any other storage system (for example, Azure Table storage), all they have do is replace the ‘UserStore’ class with an Azure Table storage implementation. There would be no additional changes needed in the AccountController, and the existing application would function seamlessly.

The problem

In the current approach, if there are two instances of the UserManager in the request that work on the same user, they would be working with two different instances of the user object. An example for this would be using the one in the class property and instantiating one locally in the method under execution. In this scenario the changes made by either of them would not reflect the changes made by the other. Hence persisting these changes back to the database would lead to incorrect changes being made to the user object.

The same problem exists when you are trying to use the DBContext class in the application.

The solution

The solution to the above problem is to store a single instance of UserManager and DbContext per request and reuse them throughout the application. Since Identity hooks into the OWIN pipeline via cookie middleware, we can store the UserManager and DbContext in the OWIN context object and retrieve them as needed.

1. In the application created above, update the Identity packages to 2.0.0-beta1 from the NuGet feed. This can be done through the Manage Nuget packages window. The steps to update Nuget packages are explained here.

2. Instead of directly working with UserManager<T> class we can define a custom class, ApplicationUserManager that extends from UserManager<T>. In the project under the App_Start folder create a new file IdentityConfig.cs and add a new class ApplicationUserManager. The ApplicationUserManager should extend the UserManager class

Code Snippet
  1. publicclassApplicationUserManager : UserManager<ApplicationUser>
  2.     {
  3.     }

3. The web application uses the new OWIN cookie middleware for the cookie-based authentication. During application start, the Configuration method in the Startup class is invoked, which configures all the middleware components registered in the application. In the MVC 5 template, the cookie middleware is configured through the ConfigAuth method defined in the Startup.Auth class.

Since we need to register the UserManager and DBContext class with the OWIN context during app start, we will add methods to do that in the ConfigureAuth method. The ‘CreatePerOwinContext<T>’ method is defined in the Microsoft.AspNet.Identity.Owin namespace. This method registers a static callback method which returns an instance of type <T>. This method is invoked once per request and used to obtain instance object which is used during the lifetime of the request.

4. To create a static callback method that returns an instance of DbContext , in the ApplicationDbContext class create a method as defined below

Code Snippet
  1. publicstatic ApplicationDbContext Create()
  2.     {
  3.         returnnew ApplicationDbContext();
  4.     }

5. Similarly define a method in the ApplicationUserManager that returns an instance of the ApplicationUserManager.

Code Snippet
  1. publicstaticApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
  2.         {
  3.             var manager = newApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
  4.             return manager;
  5.         }

In the constructor of the UserManager, we need to retrieve the instance of DbContext to configure the UserStore. We can get the instance of the object from the OwinContext using the ‘Get<ApplicationDbContext>’ method that in turn returns the single instance of DbContext class created using ApplicationDbContext.Create callback method.

6. Register these two callback methods in the ConfigureAuth method through the ‘CreatePerOwinContext’ method

Code Snippet
  1. publicvoid ConfigureAuth(IAppBuilder app)
  2.         {
  3.         app.CreatePerOwinContext<ApplicationDbContext>(ApplicationDbContext.Create);
  4.         app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
  5.          .
  6.          }

7. Next we hook this up in the AccountController class. Change the constructor and the UserManager property on the class to type ApplicationUserManager

Code Snippet
  1. public AccountController()
  2.         {
  3.         }
  4.         public AccountController(ApplicationUserManager userManager)
  5.         {
  6.             UserManager = userManager;
  7.         }
  8.         privateApplicationUserManager _userManager;
  9.         publicApplicationUserManager UserManager
  10.         {
  11.             get;
  12.             privateset;
  13.         }

8. In the set property of the UserManager we need can retrieve the UserManager instance from the OWIN context. For this we have an extension method provided in the Microsoft.AspNet.Identity.Owin namespace

Code Snippet
  1. publicApplicationUserManager UserManager
  2.         {
  3.             get
  4.             {
  5.                 return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
  6.             }
  7.             privateset
  8.             {
  9.                 _userManager = value;
  10.             }
  11.         }

9. Run the application and verify that a local user can be created in the application.

10. Also in the application if we need to work with the DbContext object directly we can get the instance of the class from the OWIN context as mentioned earlier using the ‘Get<T>’ method

Code Snippet
  1. var dbContext = context.Get<ApplicationDbContext>();

Configuring UserManager properties

Another advantage of following this approach is that we can configure the UserManager properties when instantiating it in a single place. For example, the UserManager by default has a password validator that validates that the supplied password is of length 6 characters. We can change this to use the new password validator in 2.0.0-beta1 which checks for additional complexity in the supplied password during registration.

To do this, simply set the PasswordValidator property in the ‘Create’ method of the ApplicationUserManager with the new ‘PasswordValidator’ class

Code Snippet
  1. public ApplicationUserManager(UserStore<ApplicationUser> userStore)
  2.             : base(userStore)
  3.         {
  4.         }
  5.         publicstaticApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
  6.         {
  7.             var manager = newApplicationUserManager(newUserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
  8.             manager.PasswordValidator = newPasswordValidator
  9.             {
  10.                 RequiredLength = 10,
  11.                 RequireNonLetterOrDigit = true,
  12.                 RequireDigit = true,
  13.                 RequireLowercase = false,
  14.                 RequireUppercase = false,
  15.             };
  16.             return manager;
  17.         }

Here the PasswordValidator class is configured to verify that the supplied password has a non-alphanumeric character and a numeric character. Also the length of password should be of 10 characters length. Similarly other properties of the UserManager can be configured to be persisted through the context lifecycle.

Summary

This post shows how to get a per-request, single instance of the UserManager and DbContext classes from the OWIN context to be used throughout the application. This will form a base for additional blog posts outlining the new features in ASP.NET Identity 2.0.0-beta1.

I hope you have found this walkthrough useful. If you have any questions around ASP.NET Identity or find issues, please feel free to open bugs on the Identity Codeplex site, https://aspnetidentity.codeplex.com/ , leave comments on this blog, or ask questions or StackOverflow (tag: aspnet-identity). I can also be reached on twitter (@suhasbjoshi).



Viewing all articles
Browse latest Browse all 7144

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>