PAGES

31

Oct 12

The SimpleMembershipProvider Plays Nice with EF Code First and Azure



We’ve been looking into some of the plumbing for a new project.  One part of the solution is an ASP MVC site hosted in Azure.  We’re plan to have our own membership database so we started looking at the Universal Providers which we’ve used successfully in the past with Azure hosted MVC solutions.  While some of the MVC templates are wired up to the Universal Providers, the MVC 4 Internet Application is configured to use the SimpleMembershipProvider

I thought I’d use this post to list some thoughts, notes and related resources about it. Based on my POC I found it plays nicely with CodeFirst and Azure and will walk through some changes I made from the default template’s code.

First I wanted to understand if this is the evolution of the ASP providers?  Beyond the fact that it is now the default in the template, I think this are article provides some background: SimpleMembership, Membership Providers, Universal Providers and the new ASP.NET 4.5 Web Forms and ASP.NET MVC 4 templates

Next I wanted to see how its data model interacted with my data model.  This is where the “Simple” comes in to play.  What’s nice  is that you can just create your own user profile table and “register” it with the SimpleMembershipProvider through the WebSecurity class.  Here you see my very simple data context and User model and configuration class:

public class MVCApp3Context : DbContext
{
    public MVCApp3Context()
        : base(ConnectionStringHelper.GetConnectionString())
    {
    }

    public DbSet<Customer> Customers { get; set; }
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Configurations.Add(new UserConfiguration());
    }
}

public class User
{
    public int UserID { get; set; }
    public string UserName { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class UserConfiguration : EntityTypeConfiguration<User>
{
    public UserConfiguration()
    {
        ToTable("User");
        Property(u => u.ID).HasColumnName("UserID").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

What’s nice is that it is using my model for user info and its own table for password info.  When a user is set up via the API you can just pass an anonymous object with the additional properties (i.e. the First and Last name from your registration model).  That means I can treat the calls for login as a black box, but I don’t have to do some hack to pull together the membership table and my own user table data to show the list of users in an Admin page.  This might include name, email, any other attribute I track about a user.  There is no Application table anymore, so tracking the tenant or app or other segregation of users would be another example.  One other note here is that UserID has to be an int.  I don’t personally like that because in general I use Guids for IDs, but it isn’t the end of the world.

To start getting everything hooked up, in Global.asax I set the initializer for my context and initialize it. 

protected void Application_Start()
{
    Database.SetInitializer<MVCApp3Context>(new MVCApp3ContextInitializer());
    using (MVCApp3Context db = new MVCApp3Context())
    {
        db.Database.Initialize(false);
    }
    ...
}

Then I restructured the code that default template generated, getting rid of the InitializeSimpleMembershipAttribute and instead make the call to WebSecurity to use my User table in the Seed of my initializer.  This will create the Membership tables after my model tables and also allows me to seed some users.  This provides a HUGE efficiency for a dev team and is why I love Code First.  I don’t like the prod or test environment tables being created or seeded like this, but during dev iterations this is great.

public class MVCApp3ContextInitializer : DropCreateDatabaseIfModelChanges<MVCApp3Context>
{
    protected override void Seed(MVCApp3Context context)
    {
        base.Seed(context);
        SeedMembership();
    }

    private void SeedMembership()
    {
        // Use the overload that allows for connection string to be passed in
        WebSecurity.InitializeDatabaseConnection(ConnectionStringHelper.GetConnectionString(), "System.Data.SqlClient", "User", "UserID", "UserName", true);
        WebSecurity.CreateUserAndAccount("jdoe99", "password99", new { FirstName = "John", LastName = "Doe" });
    }
}

Finally I was happy that with this provider that I didn’t have to rely of web.config for connection strings.  Unless something has changed, the Universal Providers expect a connection string name in code and then look it up based on what is defined in the connectionstrings section of your config.  This was annoying when using an Azure web role, because it requires some start up logic to swap out the connection string in the web.config that is in your package as you move environments instead of just keeping it in the Azure config file (less of a pain if you’re using the Azure Web Sites which are still only in Preview).  As you can see in the code above there is an overload that allows me to have my connection string come from wherever I want.

After making these changes I was able to deploy locally or to Azure and to local SQL or SQL Azure.

The only other thing that jumped out at me in my testing was that the PasswordSalt column was suspiciously empty in the db:

This explained how passwords are indeed being salted for hashing and I felt better: The SimpleMembershipProvider, Secure Passwords and the Crypto Helper

0 comments , permalink


Tagged , , , ,