Seeding & Customizing ASP.NET MVC SimpleMembership

In ASP.NET MVC 4 Microsoft introduced a new membership provider that is referred to as SimpleMembership.  SimpleMembership makes it easier to customize user profiles and incorporates OAuth support.  I have seen a lot of questions on StackOverflow on how to customize SimpleMembership and seed it with data and in this post I will discuss some methods for achieving both .

SimpleMembership uses Entity Framework (EF) 5 and the code-first model. To initialize the database that contains the users and roles a filter is used called InitializeSimpleMembershipAttribute.  This attribute is decorated on the AccountController as shown here:

[Authorize]
[InitializeSimpleMembership]
public class AccountController : Controller
{
   ...
}
 
This is setup this way so that the initialization code is only called when accessing the actions on the AccountController.  If you look at the code for InitializeSimpleMembershipAttribute you will see a lot of code to setup initialization which is basically there so that your MVC application will behave correctly if Forms Authentication is not used.  If you will always be using Forms Authentication we can eliminate most of this code and simplify things. I have not been able to verify it but I think the way that this code initializes the database is the reason I have seen inconsistent behavior where modifying the UserProfile class is not reflected in the database schema.  You can read more about this issue here.

In order to follow along with these directions for seeding and customizing SimpleMembership create a new MVC 4 project in Visual Studio and select the Internet option.  The first thing we do is remove the InitializeSimpleMembershipAttribute from the AccountController that is shown above.

Next we need to create our own database initializer.  Add a new class to the project called  InitSecurityDb.  Now add the following code to this class.

using System.Web.Security;
using SeedSimple.Models;
using WebMatrix.WebData;

namespace SeedSimple
{
    public class InitSecurityDb : DropCreateDatabaseIfModelChanges<userscontext>
    {
        protected override void Seed(UsersContext context)
        {
            
            WebSecurity.InitializeDatabaseConnection("DefaultConnection",
               "UserProfile", "UserId", "UserName", autoCreateTables: true);
            var roles = (SimpleRoleProvider)Roles.Provider;
            var membership = (SimpleMembershipProvider)Membership.Provider;

            if (!roles.RoleExists("Admin"))
            {
                roles.CreateRole("Admin");
            }
            if (membership.GetUser("test", false) == null)
            {
                membership.CreateUserAndAccount("test", "test");
            }
            if (!roles.GetRolesForUser("test").Contains("Admin"))
            {
                roles.AddUsersToRoles(new[] { "test" }, new[] { "admin" });
            } 

        }
    }
}

The call to WebSecurity.InitializeDatabaseConnection method is required to setup the database connections and information required by SimpleMembership.   After initializing the database we can seed it with the information we need to run our application in the overriden Seed method.  Here we create and Admin role and a test user and then map the role to this user.  Note that I have used the DropCreateDatabaseIfModelChanges type of initializer so that the database will only be created if our entities change. We will cover more on changing the UserProfile entity later on. You could change this to DropCreateDatabaseAlways if you want the database to always be recreated on application start-up.

To kick off this initialization we need to add some code to the Application_Start method in the Global.asax. Here is the code we need to add:

 Database.SetInitializer<userscontext>(new InitSecurityDb());
 UsersContext context = new UsersContext();
 context.Database.Initialize(true);
 if (!WebSecurity.Initialized)
    WebSecurity.InitializeDatabaseConnection("DefaultConnection",
         "UserProfile", "UserId", "UserName", autoCreateTables: true);


Notice that there is a check to make sure if WebSecurity is initialized and if it is not we go ahead and do it. We need to check this because our Seed method will only be called if the database model has changed.

That is all you need to do to seed the SimpleMembership database at application start-up.  Lets test this out by adding an AuthorizeAttribute on the Contact action in the HomeController.  The changes to the HomeController will look like this:

 [Authorize(Roles="Admin")] 
 public ActionResult Contact()
 {
      ViewBag.Message = "Your contact page.";
      return View();
 }

Compile and start your application in the debugger.  Click on the Contact tab.  Since we have added the Authorize attribute on this action it should take us to the logon page.  Enter the test user name and password that was seeded in the database and hit Enter. This should log you on and take you to the Contact tab.

You have now successfully seeded the SimpleMembership database.  Now lets see how to customize the user profile. To do this go to the Model folder and open AccountModels.cs.  In there is a class called UserProfile.  Lets modify it so that we can also store the email address for a user. Our code will look something like this:

 [Table("UserProfile")]
 public class UserProfile
 {
     [Key]
     [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
     public int UserId { get; set; }
     public string UserName { get; set; }
     public string Email { get; set; }
 }

Now run the application again. When you run the application EF will determine that the model has changed and will drop the database and recreate it.  To view the changes select "Show All Files" in the Solution Explores and you will see a database in the App_Data folder with the name specified in the web.confg for DefaultConnection.  Open the database and look in the UserProfile table.  You should see the new column titled Email as shown in this figure.


Here is some sample code to show we can access the email properties from UserProfile.

var context = new UsersContext();
var username = User.Identity.Name;
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == username);
var email = user.Email;

That is all there is to seeding and customizing SimpleMembership security in your ASP.NET MVC application. This is much easier than the old method of creating custom membership and role providers.

You can download the source code for this Visual Studio 2012 project here.

To learn how to use these customization features to add email confirmation during registration to an ASP.NET MVC 4 Internet Application, read this post.

Popular posts from this blog

Using Claims in ASP.NET Identity

Adding Email Confirmation to ASP.NET Identity in MVC 5

Customizing ASP.NET Identity in MVC 5