Creating ASP.NET Users Programmatically

One area in ASP.NET I see a lot of developers struggle with it deploying their sites. Compiling the application and then FTP-ing your files to a remote host is pretty easy. Then you need to set up a database. Often this is done by your ISP so you don't have to worry about that a lot either. But then you need to move your initial data from your development system to the remote server. By using the Database Publishing Wizard - now an integral part of Visual Studio 2008 - this is usually straight forward. You export the data to T-SQL Create and Update scripts and then execute those against the production database. While this works for most of your data, it doesn't always work correctly for the Membership and Roles data that is stored in your database. When it's not working, you typically do see your regular data in your application, but you can't seem to log in with the accounts you created on your local machine using the Web Site Administration Tool.

ASP.NET Configuration Settings

One of the reasons for this behavior is a mismatch between the application on the development server and the production server. By default, the ASP.NET Provider applications (Membership, Roles, Profile, and so on) create an entry in the aspnet_Applications table named after the current web site. On a local machine, you often run your site with an URL like http://localhost:43423/WebSite1. The WebSite1 in the URL results in an application called WebSite1 in the database.

When you move the site to the production environment, the site is often accessible through an address like http://www.yourdomain.com/. This results in an application called /. When you now try to log in in your production site, ASP.NET will try to match your credentials against the current application: /. Since the initial account was created against an application called WebSite1, your account cannot be validated and you're not allowed to login.

One way around this is to set the applicationName attribute to a fixed value in your web.config file. The following code snippet shows a default implementation for Membership, Roles and Profile, all with their applicationName set to /. If you use this setting before creating accounts, you minimize the risk of a mismatch between users in the development and production environments as both environments now use the same name.

<membership defaultProvider="AspNetSqlMembershipProvider">
  <providers>
    <clear />
    <add
      name="AspNetSqlMembershipProvider"
            type="System.Web.Security.SqlMembershipProvider, 
            System.Web, Version=2.0.3600.0, 
            Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
      connectionStringName="LocalSqlServer"
      applicationName="/"
      requiresUniqueEmail="false"
      passwordFormat="Hashed"
      enablePasswordRetrieval="false"
      enablePasswordReset="true"
      requiresQuestionAndAnswer="false"
      passwordStrengthRegularExpression=""
      minRequiredPasswordLength="1"
      minRequiredNonalphanumericCharacters="0"
      passwordAttemptWindow="10"
      maxInvalidPasswordAttempts="5"
      description="Stores and retrieves membership data 
            from a local Microsoft SQL Server database."
    />
  </providers>
</membership>

<roleManager
  defaultProvider="SqlProvider"
  enabled="true"
  cacheRolesInCookie="true"
  cookieName=".ASPROLES"
  cookieTimeout="30"
  cookiePath="/"
  cookieRequireSSL="false"
  cookieSlidingExpiration="true"
  cookieProtection="All"
  >
  <providers>
    <add
      name="SqlProvider"
      type="System.Web.Security.SqlRoleProvider"
      connectionStringName="localSqlServer"
      applicationName="/"
    />
  </providers>
</roleManager>

<profile defaultProvider="AspNetSqlProfileProvider">
  <providers>
    <clear/>
      <add
        type="System.Web.Profile.SqlProfileProvider, 
        System.Web, Version=2.0.0.0, 
        Culture=neutral, 
        PublicKeyToken=b03f5f7f11d50a3a"
      name="AspNetSqlProfileProvider"
      connectionStringName="localSqlServer"
      applicationName="/"
    />
  </providers>
  <properties>
    <add name="FirstName" type="System.String"/>
  </properties>
</profile>		

Creating Users Programmatically

Another way to solve this problem is to create users programmatically using the Membership and Roles APIs. These APIs always connect to the currently configured application so users are always created where you need them. A good place to do this is to create the users and roles on application startup, in the Application_Start event handler in the Global.asax file.

C#
void Application_Start(object sender, EventArgs e)
{
  const string adminRoleName = "Administrators";
  const string adminUserName = "YourUserName";
  const string adminPassword = "P4s$w0rd";

  if (!Roles.RoleExists(adminRoleName))
  {
    Roles.CreateRole(adminRoleName);
  }

  if (Membership.GetUser(adminUserName) == null)
  {
    Membership.CreateUser(adminUserName, adminPassword);
  }

  if (!Roles.IsUserInRole(adminUserName, adminRoleName))
  {
    Roles.AddUserToRole(adminUserName, adminRoleName);
  }
}

VB.NET
Const adminRoleName As String = "Administrators"
Const adminPassword As String = "P4s$w0rd"
Const adminUserName As String = "YourUserName"

If Not Roles.RoleExists(adminRoleName) Then
  Roles.CreateRole(adminRoleName)
End If

If Membership.GetUser(adminUserName) Is Nothing Then
  Membership.CreateUser(adminUserName, adminPassword)
End If

If Not Roles.IsUserInRole(adminUserName, adminRoleName) Then
  Roles.AddUserToRole(adminUserName, adminRoleName)
End If

This code simply creates a new role called Administrators. It then adds a new user and associated that user with the new role. Directly after this code has fired, you can login using the user name and password used in this code snippet.

Every time the web application starts for the first time (after a reboot, or after an application pool restart for example) this code is executed. To avoid the code from crashing if the user or role already exists in the system or if the user is already assigned to the role, it checks the database first (using RoleExists for the role, GetUser for the user account and IsUserInRole for the role assignment).

Obviously, it's not a good idea to store user names and passwords in clear text files on your server, so be sure to remove this code again from your Global.asax file as soon as the user and roles have been created successfully.

A Note for Windows Vista and Windows Server 2008 Users

If your site runs under IIS 7 on Windows Vista or Windows Server 2008, you can use the management tools of IIS to manage your ASP.NET user accounts. For more information, open up the IIS Manager, locate your web site, and then open items like .NET Users, .NET Roles, and .NET Profile. The dialogs are pretty straight forward, so you probably don't need the help files, but if you do, they are only one click away.


Where to Next?

Wonder where to go next? You can post a comment on this article.

Doc ID 472
Full URL https://imar.spaanjaars.com/472/creating-aspnet-users-programmatically
Short cut https://imar.spaanjaars.com/472/
Written by Imar Spaanjaars
Date Posted 09/28/2008 19:52

Comments

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the Doc ID of the document.

(Plain text only; no HTML or code that looks like HTML or XML. In other words, don't use < and >. Also no links allowed.