With the introduction of ASP.NET WebPages and the WebMatrix stack our team has really be focusing on making things simpler for the developer. Based on a lot of customer feedback one of the areas that we wanted to improve was the built in security in ASP.NET. So with this release we took that time to create a new built in (and default for ASP.NET WebPages) security provider. I say provider because the new stuff is still built on the existing ASP.NET framework. So what do we call this new hotness that we have created? Well, none other than SimpleMembership. SimpleMembership is an umbrella term for both SimpleMembership and SimpleRoles.

diagram

To the left is a diagram (you can click on it to see a bigger version) I often use when explaining how SimpleMembership works to other team members. I will admit I spent time making sure my boxes looked like boxes and had four points just for you guys. The middle section dotted in blue is the existing Membership APIs in the ASP.NET Framework. The SimpleMembership APIs are implemented as providers that are plugged into the core ASP.NET APIs. SimpleRoleProvider simply implements the RoleProvider abstract base class and does not add anything more. SimpleMembershipProvider is a bit trickier. To be able to support features I will discuss later in the post we needed to add additional functionality so we created a new ExtendedMembershipProvider abstract class. The ExtendedMembershipProvider abstract class inherits from the core ASP.NET MembershipProvider abstract base class. When you are using ASP.NET WebPages both of these new providers are registered as the default Membership and Role Providers. We also added a new WebSecurity class which provides a nice façade to SimpleMembershipProvider. This class is a helper class designed to make some of the more common tasks easy when you are trying to secure your website. WebSecuirty also holds APIs for some of the new functionality that we have added. For instance confirming an account and initializing the database (I’ll talk about that in just a minute). That is the basic overview of the internals to the new SimpleMembership feature.

Okay now that you have a basic understanding of how and why we created SimpleMembership I would like to spend some time walking you through how you can use it to secure your brand new ASP.NET WebPages site. There’s too much to cover in a single blog post so I’ll skim over some areas and ignore others, but will provide you enough information so that you can get started using SimpleMembership on your own. Also I do assume that you have a basic working knowledge of ASP.NET WebPages and the patterns that are used with this type of framework. Okay so what am I going to show? I will walk you through how to set up the database, create a registration page, create the login and logout pages, create a profile page, and finally create a simple admin section to control it all.

Before we start the first step of setting up your database I would like to give you some background information. One of the biggest complaints from customers has been the lack of control over the users table. The default provider in ASP.NET gave you some generic information and then the ability to store other information in a “blob”. This worked out well if you were not trying to do anything complicated but, if you wanted to something complicated it wasn’t that easy. With SimpleMembership we decided that it was easier to allow you, the developer, to have control of the users table and let SimpleMembership handle storing the authentication credentials. For example, you might already have a users table and want to integrate it with SimpleMembership. All SimpleMembership requires is that there are two columns on your users table so that we can hook up to it – an “ID” column and a “username” column. The important part here is that they can be named whatever you want. For instance username doesn’t have to be an alias it could be an email column you just have to tell SimpleMembership to treat that as the “username” used to log in.

UsersTable Okay so let’s get started creating our users table. In this example I will be using the new SQL CE 4 Database Engine so in WebMatrix add a new database file (this can be done on the database manager tab), name it whatever you like in this example I chose “SecurityDemo.sdf”.  Add a new table called “Users” (once again you can call the tables and columns whatever you want I’m just guiding you through my example). The only two columns you most have are some ID column and some username column in this case I chose the very clever names “UserID” and “Username”. Also add any other columns for data you would like to store, when you’re done you’ll have something like the table in the image to the left. Although if you want to follow along add all the columns I have that way it will be easier to copy and paste code.

Now that we have created our users table we need to wire it up to SimpleMembership so that SimpleMembership knows what columns to use. Now I’m going to introduce a new feature of ASP.NET WebPages that you may or may not know about which is the start page. This is a specially named page that will get run the first time an application starts. So create a page with the name “_start.cshtml” in your site, remove all the markup leaving only the code block at the top, and put the following code in that code block.

  1.  //Set up Simple Membership
  2. WebSecurity.InitializeDatabaseFile(“SecurityDemo.sdf”, “Users”, “UserID”, “Username”, true);

 

MembershipTablesCreated The parameters are rather self-explanatory but they are, in the following order,  name of the database file, name of the table you are using for the users table, name of the column being used for the ID, and name of the column you are using for the username. If you notice I left the last one off because it isn’t really self-explanatory, it is a bool value telling SimpleMembership to create the tables it needs if they are not present. These are tables that are used under the covers to make everything work, for instance the roles table. Every table created by SimpleMembership will start with “webpages_” in the name. As a general rule of thumb for later on if you have to manually query one of the tables with “webpages_” in the name there is probably a better API to be calling. Now it’s time to run your site once, these will cause SimpleMembership to go and create the tables we need. When you’re done you will have the tables in the image to the left in your database.

Okay so now you’re all set up and SimpleMembership is wired up to your table. The next logical step is to create a page where users can register for your site. Due to the fact that his post is already getting long I’m not going to show you the whole page, just the key pieces of code and if you’d like to see the whole page you can download the sample and use it to follow along. At this point create a register page with input fields for each of the columns in your user table. When the page is posted to you want to do some sort of validation on the values, you can download the sample and see the pattern I use. If all the posted values are valid call the following code, where the anonymous object represents the columns in your users table.

  1. WebSecurity.CreateUserAndAccount(username, password
  2.    new{FirstName = fname, LastName = lname, Email = email, StartDate = DateTime.Now, Bio = bio});

Now is the perfect time to talk about the words User and Account. Basically the word user maps to the user table you create while the word account maps to the table SimpleMembership uses to store things like password salts and confirmation tokens. You can choose to use your own SQL insert statement to insert into your users table and then just wire that entry up to SimpleMembership but as this is a rather common task we created a helper for it.

The next logical set would be to create a way to login and logout of your site. Let’s start with the login page. Once again I’m skipping all the simple HTML markup and post data validation since we have a lot to cover.

  1. if(WebSecurity.Login(username, password)){
  2.    var returnUrl = Request.QueryString["ReturnUrl"];
  3.    if(returnUrl.IsEmpty()){
  4.        Response.Redirect(“~/Account/Profile”);
  5.    } else {
  6.        Response.Redirect(returnUrl);
  7.    }
  8. }

There is a helper method that hangs off of the WebSecurity class that you use to login a user and it will return true of false based on if the username and password combination is valid. There is one thing that I wanted to make sure that I pointed out here. If you remember that SimpleMembership is built on the core ASP.NET Membership APIs when a user visits a part of the site that requires authentication (I’ll cover how to set that up in just a minute) they will be redirected to the the login page (default is “~\account\login”). When they are redirect the framework tacks on a query string parameter of “ReturnUrl” that can be used to return the user to the URL that they were trying to navigate to after they are logged in. You’ll notice that in the code I check for that and if it’s not empty I redirect them to that URL otherwise they get redirect to their profile page.

Now that you can login to the site you need someone to be able to logout right? Logging out is much simpler, in fact the logout page does not have any markup it is simply a page that the website posts to and then the request is redirected after the user is logged out.

  1.  WebSecurity.Logout();
  2. var returnUrl = Request.QueryString["ReturnUrl"];
  3. if(returnUrl.IsEmpty()){
  4.     Response.Redirect(“~/”);
  5. } else {
  6.     Response.Redirect(returnUrl);
  7. }

I choose to keep the concept of a return URL because I wanted a user to be returned to the page where they clicked on the link to logout. This makes for a nice touch and can really help user experience but it’s not necessary, you could simple redirect them to the root of the site.

What good is having a login and logout page if there is no part of your site that requires a user to be authenticated? In this example I choose to kill two birds with one stone and say that if a user is logged in and they visit their profile page they will be able to change their password. This way I get to show you guys two APIs instead of just one. For a cleaner look I have a conditional statement in the HTML markup that will leave out the Change Password markup if “isCurrentUser” is false.

  1.  //Get the username from the URL e.g. account/profile/osbornm
  2. var UserName = UrlData[0] != string.Empty ? UrlData[0] : WebSecurity.CurrentUserName;
  3. var isCurrentUser = UserName == WebSecurity.CurrentUserName;
  4. if(IsPost && isCurrentUser){
  5.     //If everything checks out try to change the password
  6.      if(val.Count == 0){
  7.          if(!WebSecurity.ChangePassword(WebSecurity.CurrentUserName, oldPassword, newPassword)){
  8.              val.Add(“Unable to change password.”);
  9.          }
  10.      }
  11.  }

There are a couple of APIs that I would like to point out in the code above. First off is that CurrentUserName property hanging off of WebSecurity. If a user is logged in this will be there username and if they are not logged in it will be an empty string. Also there are plenty of these helpers methods that you can use including one that requires an authenticated user that you can put at the top of your page to require a user to be logged in to see that page. The second API I want to call out is ChangePassword, also hanging off of WebSecurity. This will return true is the change was successful or false if there was a problem.

Now that we have the basic functionality the only thing left is to spice it up by adding some roles and a special section for administrators to do administrator stuff. The way I’m going to tackle this problem is to create parts of the admin section and then use them to add the role and add users to the role, once that’s done we will change it so only people in the admin role can access that page. Just like before I will leave off the simple HTML markup and you can download the sample if you would like to see it.

  1.  //Code to add a role
  2. var roleName = Request["roleName"];
  3. if(roleName != null){
  4.     if(roleName == string.Empty){
  5.         //Validation Message
  6.     } else {
  7.         Roles.CreateRole(roleName);
  8.     }
  9. }

The first thing I would like to point out is that pattern that I am using for this page. I wanted to have all the functionality on one page so that means there will be many forms all posting to this page. So how to I tell the difference from a post to add a role from a post to add a user to a role? I use the values that come in with the request. If the post has a value named “roleName” I know that it must have come from that form, that is what line three is testing. While this isn’t fool proof, it is simple and works most of the time and its fine for this sample. You’ll notice that in the code sample we have reached a point where we aren’t using the WebSecurity class anymore, that’s because the core ASP.NET APIs work just fine. So we call the static “CreateRole” method on the Roles object, which under the covers ends up calling the SimpleRoleProvider to create the role.

Now that we can create a role we need a way to add a user to that role otherwise there is no reason to have roles. I choose a simple UI for this task that probably isn’t the most efficient for adding lots of users to lots of roles but it works fine for this example. So you’ll notice in the code below that I grab all the users and all the roles so that a dropdownlist can be populated with the values.

  1.  //Code to get all users
  2. dynamic users;
  3. using(var db =  Database.Open(“SecurityDemo”)){
  4.      users = db.Query(“SELECT * FROM [Users]“);
  5.  }
  6. //Get the current roles in the system
  7. var roles = Roles.GetAllRoles();
  8. //Code to add a user to a role
  9. var userToAddTo = Request["userToAddTo"];
  10. var roleToAdd = Request["roleToAdd"];
  11. if(!userToAddTo.IsEmpty() && !roleToAdd.IsEmpty()){
  12.     Roles.AddUserToRole(userToAddTo, roleToAdd);
  13. }

The reason I choose to query for all the users myself is I wanted to show you that the developer really does own the users table so you can issue normal SQL queries to get info like this without having to go through all the Membership APIs. Again, I followed the pattern of detecting values that came in from the request to determine what action needed to be performed. Once again the core ASP.NET APIs are good enough for adding a user to a role so you can simply call the static method on the Roles object.

Now that we have can go create the admin role and add our user to it, navigate to the page and do so, it’s okay I’ll wait for you. Done? Good. So now that you’re an admin on your own site let’s go lock down this admin section of the site so only users that are in the admin role can access it. This is easiest done by moving that page to an admin folder and then using an init page. This is another one of those specially named pages that you may or may not already know about. Basically you create a page with the name “_init.cshtml” in a folder and that page will run first before any other page in that folder for every request. This is useful for doing operations such as requiring an authenticated user or requiring a specific role because the code is in one location and not spread across every page.

  1. WebSecurity.RequireAuthenticatedUser();
  2. if(!WebSecurity.IsCurrentUserInRole(“Admin”)){
  3.     Response.Redirect(“~/Account/unauthorized”);
  4. }

Just like the “_start.cshtml” file there is no markup just a code block at the top. This code does two things; the first is to require an authenticated user this means if you visit part of the admin section and you aren’t logged in you will get a nice prompt for your password. The second is that if a user is authenticated but they aren’t in the “admin” role they will be redirect to an unauthorized page I created that displays a nice error message explaining why they can see that part of the site. You don’t have to do it that way you could redirect anywhere you wanted for even set the status code to 404 so it looked like there wasn’t anything there, it’s all up to you.

Well now you’re done, you have a very simple implementation that secures your site and can store user information. The sky really is the limit here, there is so much more you can do, there was just too much to cover in one blog post. If you have requests for how to do something I’d be glad to add an example or even create a new post about if you just have to let me know. Also in the downloadable source there is quite a bit more than what I covered here! I have examples of requiring a confirmation email to be sent, deleting user and roles, using a layout page to display a login/logout status widget, and how to implement some simple validation based on the way ASP.NET MVC does its validation.

 

19 Responses to Using SimpleMembership With ASP.NET WebPages

  1. Riaan says:

    Very good article. Thank you very much. I managed to adapt your ideas to work with MVC4, Razor, VB and .NET 4.5. Works like a charm with the new SimpleMembership model.

    • Patricia says:

      Could you please help me to do the same? It’s been driving me crazy, as I can’t find any updated step-by-step guide to implement roles in my asp.net4.5+mvc4+razor internet application.
      You’d really help my mental health :P

      • skrillex says:

        It’s as easy as you see here. It’s explained step by (dub)step.
        Anyways, you can always use google to find out more.

  2. Corey says:

    I don’t see a download link..did I just miss it?

    • Matthew says:

      Sorry I recently switched around my blog and haven’t got to fixing all the links. However the default provider in ASP.NET 4.5 / Visual Studio 2012 templates is the Simple Membership Provider so you can just create a website and look at it there.

  3. Rasmus says:

    Just wanted to add that the _start.cshtml-file allready existed for me by another name: _ViewStart.cshtml.

    I just added the WebSecurity.InitializeDatabaseFile(..)-line in the top and it worked out great.

    Great article btw

    • Matthew says:

      Simple membership is enabled by default in all the ASP.NET templates now. It probably more accurate and update to date to just create a new asp.net 4.5 project :)

  4. Guy says:

    Great article, but the full sample would be of great help- is there a chance to re-add it?

    Thanks

  5. Alan says:

    Hi Matthew

    Our SQL Server Server has been down for a few days and I have no way of knowing when they’ll eventually sort it out and get it back up again.

    So, the catch is that I can’t test working with my existing Users table (in the legacy SQL 2005 database of existing users from an obsolete .asp system that I’m trying to evolve to MVC4) – but I suspect that WebSecurity.InitializeDatabaseFile(“MyDatabaseName.mdf”, “Users”, “UserID”, “Username”, true); is not going to work once they restore the server? (Although the table name and the field names in the legacy Users table happen to be valid).

    Surely, the SQL Server Server name will need to be qualified with a connection string and a valid user and valid password to be able to have rights to access that Users table in my inherited existing legacy SQL 2005 database?

    The Sql Server server is NOT on my development PC – it’s on the network – so, how would my MVC4 app on my PC know where to find the relevant database just from the name of the database?

    If I’m right, what would the correct syntax be to enable my existing legacy Users table out on the network to be used for Simple Membership, please, Matthew?

    I understand from one of the comments that this WebSecurity “connection string” needs to be placed in _ViewStart.cshtml in my MVC4 app.

    I’m using VS2010 still and still .Net 4.0 (would you believe on XP!) ;-)

    Alternatively, is there maybe some other less elegant way to securely persist state to enable authentication of users and their roles in MVC4 to restrict access to sensitive aspects of my MVC 4 replacement app?

    I will appreciate very specific instructions (since as you will have guessed, I’m a newbie to MVC4 and am still trying to find my feet – while being pressurised to get the app finished to get on with other stuff).

    Regards

    Alan

    • Alan says:

      SQL Server Server is now finally back up – but ‘WebMatrix.WebData.WebSecurity’does not contain a definition for InitialiseDatabaseFile.

      Found an ‘InitialiseDatabaseConnection’ with Intellisense.

      Then Googled “WebSecurity.InitializeDatabaseConnection mvc 4″ and found

      if (!WebSecurity.Initialized)
      {
      WebSecurity.InitializeDatabaseConnection(“DefaultConnection”, “UserProfile”, “UserId”,
      “UserName”, autoCreateTables: true);
      }

      at

      http://notebookheavy.com/2012/08/22/mvc-4-authentication/

      which says it must go in _AppStart.cshtml which you need to create in the root of your MVC4 app. (I went with putting it in the existing _ViewStart.cshtml).

      Haven’t tested yet – but at least no errors were generated, which is already an improvement – so I thought I should leave this hint for anyone else with my predicament.

  6. Dave says:

    I have this working but am NOT impressed with the not so simple SimpleMembership provider. I had to add the UserProfile table I wanted to my data model as the SimpleMembershipInitializer would only add the standard Id and Name fields and none of my custom fields. This all seems very half baked just to rush out some oauth support.

    - The schema is bloated with fields that are not used (e.g. salt and oauth)

    - Oauth is not optional

    - Does not create UserProfile custom fields for an existing model first database.

    - Geared for code first, not for model first.

    - Code is based on the old Membership provider rather than new code.

    - No support for seeding or user/profile maintenance like the WSAT tool which it is incompatible with.

    - Continuous unnecessary overhead checking if the database exists and if the tables exist, all for naught even on a code first approach and certainly for model first. I commented that out.

  7. ismar says:

    This is a greatly simplified way of using existing dbs with asp.net membership.
    Is there any way that a table with a composite primary key (say 2 columns) could be used with the
    WebSecurity.InitializeDatabaseFile method to store user data?

  8. ismar says:

    Also,
    If the user table’s user id column is not named UserId,
    the WebSecurity.InitializeDatabaseFile fails to create the _UsersInRoles table (possibly because of some assumption on the fk name)

  9. James says:

    The biggest issue I have with SimpleMembership is the use of integer types for User ID. Surely Guids would have been preferable. Integers in the long term are not a good data-type if you want to look at DB federating on a platform like Azure. It seems like a big backwards step. I have now been trawling the net looking for a way to see if I can implment Guids in SimpleMember as user identifiers instead of ints and have not found anything, so if anyone else has any ideas I’d love to be pointed in the right direction. I don’t want to have to implement my own model just to change the basic column type.

    • Matthew says:

      Int was chosen to make it simple. Honestly it’s pretty simple to create your own inheriting from Simple Membership and overriding the data type.

  10. Won says:

    Best article on SimpleMembership I’ve seen! Thanks!

  11. Kevin Russell says:

    Take a step back, enroll in school and learn to code. This is way more trouble than it is worth.

  12. David says:

    Please put the download code up!
    I am trying to get this running in VS2010 standard web application and the WebSecurity.InitializeDatabaseConnection wors, it made the other tables on the sql server for me, but a call to WebSecurity.CreateUserAndAccount generates an error (To call this method, the “Membership.Provider” property must be an instance of “ExtendedMembershipProvider”.).

    Any idea why?

  13. Oscar says:

    Hi,

    great post. Is this targeting only the .NET Framework 4.5 and above? What if I want to use it with the .NET Framework 4.0, is that possible?

    kind regards
    Oscar

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>