Windows Live ID – Part 2 – Membership Provider

In the first part of this article I explained how to integrate Windows Live ID into your own web site, how to call it and what information it returned and how to access that information.

Now that is all fine and well but if you’re using ASP.Net 2.0 (and above) and you want to take advantage of the inbuilt memberships, roles etc. the information returned from Live ID doesn’t make this easy on you. So how do you do this? Well there are a number of different ways to do this but possibly the easiest is to create your own MemberShip provider and use to store and access the information.

In this article we will be building a Membership provider that you can call with the very basic information that Live ID returns to you (basically the only useful thing it returns is a static ID string that will always be consistent for your registered web site.

Extending or building your own MemberShip provider isn’t that hard, you simply create a new class that inherits from MembershipProvider and override the methods with your own code. Unfortunately there is a fair amount of code to this but most of it is fairly intuitive.

A couple of Notes before we proceed :-

  1. In my membership provider I take advantage of the Microsoft Patterns and Practices Enterprise objects for data access. This is a very good and useful code block that saves you from manually having to open and close database connections etc. It’s also completely free and can be downloaded from here. Remember you will need to copy the appropriate dll files into the bin folder of your web application and set references to them.
  2. There are a few things that you will have to do on your own depending on the information that you wish to collect from the user. I explained in a basic outline how to do this is in the previous article. Basically once a user signs in using Live ID, you should redirect to a Registration page on your site. The first thing you want to do is to check to see if the user already exists (ValidateUser method). If they do then great you can redirect the user back to where ever it was they wanted to go in your site, if not then you will want to display a form to collect the information you wish. At the minimum you will need an Email address then you create the user in your database using the CreateUser method.
  3. As a lot of the methods in the Membership provider are fairly self explanatory, I won’t take time to explain them all, they all follow the same or similar concepts as the ones I will explain.

So lets start. Create a new Class library file in your App_Code directory and name it LiveIDMembershipProvider.cs.
Now we import some base libraries.

using System;

using System.Data;

using System.Data.SqlClient;

using System.Configuration;

using System.Web;

using System.Web.Security;

using Microsoft.Practices.EnterpriseLibrary.Data;

using System.Collections.Generic;

using System.Collections.Specialized;

using System.Configuration.Provider;

using System.Web.Hosting;

using System.Web.Management;

using System.Security.Permissions;

Note the reference to Microsoft.Practices.EnterpriseLibrary.Data which was explained above. Next create your class :-

namespace Hackersoft.UserRepository

{

  public class LiveIDMembershipProvider : MembershipProvider

  {

You can give your class any namespace you wish and in the class declaration we are stating that we want to inherit from MembershipProvider.

Create some class member variables :-

    static string m_ProviderName = “LiveIDMembershipProvider”;

    static int m_InvalidPasswordAttempts = 1;

    static int m_NonAlphaCharacters = 0;

    static int m_PasswordLength = 3;

    static int m_PasswordAttemptWindow = 5;

    private string m_ApplicationName = string.Empty;

 

The m_ProviderName variable you can set to whatever you like. The other variables are fairly self explanatory, the number of password attempts you will allow doesn’t really count as we will be using Live ID for authentication, same with PasswordLength which states the minimum length of the password. We will be using a hashed version of the user ID returned by Live ID as the password.

 

Next we add some public properties to set or read (mostly read) the member variables above. The only one which is read/write is the Application Name. The way that the .Net database is setup is that you can have multiple sites running off a single database and authentication against a specific site is done by passing through the name of the application that you wish to authenticate against.

  public override string ApplicationName

    {

      get

      {

        return m_ApplicationName;

      }

      set

      {

        m_ApplicationName = value;

      }

    }

 

    public override bool EnablePasswordRetrieval

    {

      get { return false; }

    }

 

    public override bool EnablePasswordReset

    {

      get { return false; }

    }

 

    public override int MaxInvalidPasswordAttempts

    {

      get { return m_InvalidPasswordAttempts; }

    }

 

    public override int MinRequiredNonAlphanumericCharacters

    {

      get { return m_NonAlphaCharacters; }

    }

 

    public override int MinRequiredPasswordLength

    {

      get { return m_PasswordLength; }

    }

 

    public override int PasswordAttemptWindow

    {

      get { return m_PasswordAttemptWindow; }

    }

 

    public override MembershipPasswordFormat PasswordFormat

    {

      get { return MembershipPasswordFormat.Clear; }

    }

 

    public override string PasswordStrengthRegularExpression

    {

      get { throw new NotSupportedException(); }

    }

 

    public override bool RequiresQuestionAndAnswer

    {

      get { return false; }

    }

 

    public override bool RequiresUniqueEmail

    {

      get { return true; }

    }

The first method in your custom membership provider that you will want to override is the CreateUser method :-

    public MembershipUser CreateUser(string username, string password, string applicationname,

      string email, string passwordquestion, string passwordanswer, out int status)

    {

      //This calls the Membership_CreateUser SPROC.

      //The CreateUser SPROC will automatically create an application if one doesn’t exist

      //Create the User account and also create the Membership account.

      //The output to this is the newly created UserID.

      string UserID = string.Empty;

      string PasswordSalt = CreateSalt(5);

      string PasswordHash = CreatePasswordHash(password, PasswordSalt);

      status = 0;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_CreateUser”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@UserName”, username);

      cmdSQL.Parameters.AddWithValue(“@Password”, PasswordHash);

      cmdSQL.Parameters.AddWithValue(“@PassWordSalt”, PasswordSalt);

      cmdSQL.Parameters.AddWithValue(“@Email”, email);

      cmdSQL.Parameters.AddWithValue(“@PasswordQuestion”, passwordquestion);

      cmdSQL.Parameters.AddWithValue(“@PasswordAnswer”, passwordanswer);

      cmdSQL.Parameters.AddWithValue(“@IsApproved”, 1);

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@CreateDate”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@UniqueEmail”, 1);

      cmdSQL.Parameters.AddWithValue(“@PasswordFormat”, MembershipPasswordFormat.Clear);

      SqlParameter output = new SqlParameter(“@UserID”, SqlDbType.UniqueIdentifier);

      output.Direction = ParameterDirection.Output;

      cmdSQL.Parameters.Add(output);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      try

      {

 

        status = Convert.ToInt32(db.ExecuteNonQuery(cmdSQL));

        UserID = cmdSQL.Parameters[“@UserID”].Value.ToString();

 

      }

      catch (Exception ex)

      {

        //put your exception handling here

      }

      MembershipUser user = GetUser(new Guid(UserID), true);

      //status = Error;

      return user;

    }

This method returns a Membership user object (which is ultimately what you want). We call the standard SQL stored procedure provided for us and are simply poking our own custom values into this. The four main parameters that you will need to provide are username, password, application name and email. Both username and password will be the user ID returned from the Live ID service, Application Name will be the name of your application (see above for short explanation) and email is the minimum data that you must collect from the user on your registration page. Password question and password answer you can simply set to string.Empty.
So the first thing that we do is call a helper methods to create a password hash and salt so that the password is essentially encrypted (remember that the password we use will the same as the username, the user ID returned from Live ID). Next we call our Enterprise library to create a connection to the database then create a SqlCommand object stating that we wish to call the “aspnet_Membership_CreateUser” stored procedure and simply plug our data into the parameters that it expects.
After the stored procedure has been called it will return a unique user ID for this user. With this User ID we can call the GetUser method to create an actual MembershipUser object and then simply return this MembershipUser object.  That’s it, fairly simple stuff.
The CreatePasswordHash and CreateSalt methods aren’t very interesting so I’ll just include them in the full code listing at the end of this article.

There are two overrides of the GetUser method, the one that we called in the CreateUser method above takes the UserID (as a GUID) and a flag indicating whether the user is online or not and returns a MembershipUser object.

    public override MembershipUser GetUser(object provideruserkey, bool userisonline)

    {

      MembershipUser user = null;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetUserByUserId”);

      cmdSQL.Parameters.AddWithValue(“@UserID”, provideruserkey);

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@UpdateLastActivity”, userisonline);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      DataSet ds = db.ExecuteDataSet(cmdSQL);

      if(ds.Tables[0].Rows.Count > 0)

      {

        DataRow dr = ds.Tables[0].Rows[0];

        user = new MembershipUser(m_ProviderName, dr[“UserName”].ToString(),

        provideruserkey, dr[“Email”].ToString(), dr[“PasswordQuestion”].ToString(),

        dr[“Comment”].ToString(), Convert.ToBoolean(dr[“IsApproved”]),

        Convert.ToBoolean(dr[“IsLockedOut”]), Convert.ToDateTime(dr[“CreateDate”]),

        Convert.ToDateTime(dr[“LastLoginDate”]), Convert.ToDateTime(dr[“LastActivityDate”]),

        Convert.ToDateTime(dr[“LastPasswordChangedDate”]), Convert.ToDateTime(dr[“LastLockoutDate”]));

      }

      return user;

    }

 

As you can see, this is fairly much the same story as the CreateUser method described above. We call the Enterprise to create a connection to the database, create a SqlCommand object and tell it that we wish to call the “aspnet_Membership_GetUserByUserId” stored procedure, fill in the parameters that it expects then call the stored procedure. The real change from the CreateUser method described above is that once the database call has returned, we need to actually instantiate a MembershipUser object. The MembershipUser object expects a lot of parameters however all the information required is returned to us in the dataset from the database call. We simply plug these values in and have a now have a valid MembershipUser object.

 

The second override of the GetUser method is very much the same but instead of passing in a User ID we pass in the username, name of the application and again whether the user is online or not.

 

public MembershipUser GetUser(string username, string applicationname, bool userisonline)

    {

      MembershipUser user = null;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetUserByName”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@Username”, username);

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@UpdateLastActivity”, userisonline);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      DataSet ds = db.ExecuteDataSet(cmdSQL);

      if (ds.Tables[0].Rows.Count > 0)

      {

        DataRow dr = ds.Tables[0].Rows[0];

        user = new MembershipUser(m_ProviderName, username,

        new Guid(dr[“UserID”].ToString()), dr[“Email”].ToString(), dr[“PasswordQuestion”].ToString(),

        dr[“Comment”].ToString(), Convert.ToBoolean(dr[“IsApproved”]),

        Convert.ToBoolean(dr[“IsLockedOut”]), Convert.ToDateTime(dr[“CreateDate”]),

        Convert.ToDateTime(dr[“LastLoginDate”]), Convert.ToDateTime(dr[“LastActivityDate”]),

        Convert.ToDateTime(dr[“LastPasswordChangeDate”]), Convert.ToDateTime(dr[“LastLockoutDate”]));

      }

      return user;

    }

The final method that I will describe is the ValidateUser method. Again it’s not too difficult to follow :-

    public bool ValidateUser(string username, string password, string applicationname)

    {

      try

      {

          //First thing you need to do is validate the application name and get the

          //application ID from the database.

          string PasswordHash = string.Empty;

          string PasswordSalt = string.Empty;

          string HashedPassword = string.Empty;

          string ApplicationID = GetApplicationID(applicationname);

          if (ApplicationID == null || ApplicationID == string.Empty)

          {

            //Cannot find the application name therefore the user cannot

            //Exist for this application

            return false;

          }

          //Next you need to retrieve the salt and hash from the database in order to

          //verify the password.

          Database db = DatabaseFactory.CreateDatabase();

          SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetPasswordWithFormat”);

          cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

          cmdSQL.Parameters.AddWithValue(“@Username”, username);

          cmdSQL.Parameters.AddWithValue(“@UpdateLastLoginActivityDate”, 0);

          cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, System.DateTime.Now);

          DataSet ds = null;

          cmdSQL.CommandType = CommandType.StoredProcedure;

          ds = db.ExecuteDataSet(cmdSQL);

          if (ds.Tables[0].Rows.Count > 0)

          {

            DataRow dr = ds.Tables[0].Rows[0];

            //The database does not name it’s returned rows.

            //The Salt is stored in the first field returned (Password field)

            //The Hash is stored in the 3rd field returned (PasswordSalt)

            PasswordHash = dr[0].ToString();

            PasswordSalt = dr[2].ToString();

          }

          else

          {

            //No rows were returned from the database therefore

            //User does not exist.

            return false;

          }

          //Now we hash the password supplied.

          HashedPassword = CreatePasswordHash(password, PasswordSalt);

          //Now simply compare the hashed password just generated with that stored

          //in the database.  If they match then username and password are correct

          //otherwise they are not.

          if (HashedPassword == PasswordHash)

          {

            return true;

          }

          else

          {

            return false;

          }

        }

      catch (Exception ex)

      {

        // TODO: Exception Handling routine

        return false;

      }

    }

As you can see the ValidateUser method takes in the user name, password and the application name as parameters. We make a call to the GetApplicationID method which returns the internal application GUID. The reason we do this is to make sure that the application is actually registered in the database before we make the call to validate the user. This is basically a safety check. Again we call the Enterprise Library to create a connection to our Sql Database, create a SqlCommand object and tell it we wish to call the “aspnet_Membership_GetPasswordWithFormat” stored procedure, fill out the expected parameters and make the call. What we get in return is the password hash and salt we originally stored in the CreateUser method. Next we call the CreatePasswordHash algorithm again and pass through the password entered along with the Password Salt returned from the database call. If the returned password hash is the same as the password hash returned by the database call then the password is correct (which it really should be as the password and username are the same user ID returned by Live ID).

All the rest of the methods in the code below follow the same principles as those explained above. Here is the full membership provider along with the helper methods mentioned above and any others referenced in the code :-

using System;

using System.Data;

using System.Data.SqlClient;

using System.Configuration;

using System.Web;

using System.Web.Security;

using Microsoft.Practices.EnterpriseLibrary.Data;

using System.Collections.Generic;

using System.Collections.Specialized;

using System.Configuration.Provider;

using System.Web.Hosting;

using System.Web.Management;

using System.Security.Permissions;

 

/// <summary>

/// Custom Membership provider that authenticates against a

/// central user repository

/// </summary>

namespace Hackersoft.UserRepository

{

  public class LiveIDMembershipProvider : MembershipProvider

  {

    #region Properties

    public override string ApplicationName

    {

      get

      {

        return m_ApplicationName;

      }

      set

      {

        m_ApplicationName = value;

      }

    }

 

    public override bool EnablePasswordRetrieval

    {

      get { return false; }

    }

 

    public override bool EnablePasswordReset

    {

      get { return false; }

    }

 

    public override int MaxInvalidPasswordAttempts

    {

      get { return m_InvalidPasswordAttempts; }

    }

 

    public override int MinRequiredNonAlphanumericCharacters

    {

      get { return m_NonAlphaCharacters; }

    }

 

    public override int MinRequiredPasswordLength

    {

      get { return m_PasswordLength; }

    }

 

    public override int PasswordAttemptWindow

    {

      get { return m_PasswordAttemptWindow; }

    }

 

    public override MembershipPasswordFormat PasswordFormat

    {

      get { return MembershipPasswordFormat.Clear; }

    }

 

    public override string PasswordStrengthRegularExpression

    {

      get { throw new NotSupportedException(); }

    }

 

    public override bool RequiresQuestionAndAnswer

    {

      get { return false; }

    }

 

    public override bool RequiresUniqueEmail

    {

      get { return true; }

    }

    #endregion

 

    #region Member Variables

    static string m_ProviderName = “LiveIDMembershipProvider”;

    static int m_InvalidPasswordAttempts = 5;

    static int m_NonAlphaCharacters = 0;

    static int m_PasswordLength = 3;

    static int m_PasswordAttemptWindow = 5;

    private string m_ApplicationName = string.Empty;

    #endregion

 

    public LiveIDMembershipProvider()

    {

      //

      // TODO: Add constructor logic here

      //

    }

 

    public MembershipUser CreateUser(string username, string password, string applicationname,

      string email, string passwordquestion, string passwordanswer, out int status)

    {

      //This calls the Membership_CreateUser SPROC.

      //The CreateUser SPROC will automatically create an application if one doesn’t exist

      //Create the User account and also create the Membership account.

      //The output to this is the newly created UserID.

      string UserID = string.Empty;

      string PasswordSalt = CreateSalt(5);

      string PasswordHash = CreatePasswordHash(password, PasswordSalt);

      status = 0;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_CreateUser”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@UserName”, username);

      cmdSQL.Parameters.AddWithValue(“@Password”, PasswordHash);

      cmdSQL.Parameters.AddWithValue(“@PassWordSalt”, PasswordSalt);

      cmdSQL.Parameters.AddWithValue(“@Email”, email);

      cmdSQL.Parameters.AddWithValue(“@PasswordQuestion”, passwordquestion);

      cmdSQL.Parameters.AddWithValue(“@PasswordAnswer”, passwordanswer);

      cmdSQL.Parameters.AddWithValue(“@IsApproved”, 1);

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@CreateDate”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@UniqueEmail”, 1);

      cmdSQL.Parameters.AddWithValue(“@PasswordFormat”, MembershipPasswordFormat.Clear);

      SqlParameter output = new SqlParameter(“@UserID”, SqlDbType.UniqueIdentifier);

      output.Direction = ParameterDirection.Output;

      cmdSQL.Parameters.Add(output);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      try

      {

 

        status = Convert.ToInt32(db.ExecuteNonQuery(cmdSQL));

        UserID = cmdSQL.Parameters[“@UserID”].Value.ToString();

 

      }

      catch (Exception ex)

      {

        Console.WriteLine(ex.Message.ToCharArray());

      }

      MembershipUser user = GetUser(new Guid(UserID), true);

      //status = Error;

      return user;

    }

 

    public override MembershipUser GetUser(object provideruserkey, bool userisonline)

    {

      MembershipUser user = null;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetUserByUserId”);

      cmdSQL.Parameters.AddWithValue(“@UserID”, provideruserkey);

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@UpdateLastActivity”, userisonline);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      DataSet ds = db.ExecuteDataSet(cmdSQL);

      if(ds.Tables[0].Rows.Count > 0)

      {

        DataRow dr = ds.Tables[0].Rows[0];

        user = new MembershipUser(m_ProviderName, dr[“UserName”].ToString(),

        provideruserkey, dr[“Email”].ToString(), dr[“PasswordQuestion”].ToString(),

        dr[“Comment”].ToString(), Convert.ToBoolean(dr[“IsApproved”]),

        Convert.ToBoolean(dr[“IsLockedOut”]), Convert.ToDateTime(dr[“CreateDate”]),

        Convert.ToDateTime(dr[“LastLoginDate”]), Convert.ToDateTime(dr[“LastActivityDate”]),

        Convert.ToDateTime(dr[“LastPasswordChangedDate”]), Convert.ToDateTime(dr[“LastLockoutDate”]));

      }

      return user;

    }

 

    public MembershipUser GetUser(string username, string applicationname, bool userisonline)

    {

      MembershipUser user = null;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetUserByName”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@Username”, username);

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@UpdateLastActivity”, userisonline);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      DataSet ds = db.ExecuteDataSet(cmdSQL);

      if (ds.Tables[0].Rows.Count > 0)

      {

        DataRow dr = ds.Tables[0].Rows[0];

        user = new MembershipUser(m_ProviderName, username,

        new Guid(dr[“UserID”].ToString()), dr[“Email”].ToString(), dr[“PasswordQuestion”].ToString(),

        dr[“Comment”].ToString(), Convert.ToBoolean(dr[“IsApproved”]),

        Convert.ToBoolean(dr[“IsLockedOut”]), Convert.ToDateTime(dr[“CreateDate”]),

        Convert.ToDateTime(dr[“LastLoginDate”]), Convert.ToDateTime(dr[“LastActivityDate”]),

        Convert.ToDateTime(dr[“LastPasswordChangeDate”]), Convert.ToDateTime(dr[“LastLockoutDate”]));

      }

      return user;

    }

 

    public string GetUserNameByEmail(string email, string applicationname)

    {

      string Username = string.Empty;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetUserByEmail”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@Email”, email);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      Username = Convert.ToString(db.ExecuteScalar(cmdSQL));

      return Username;

 

    }

 

    public bool ValidateUser(string username, string password, string applicationname)

    {

      try

      {

          //First thing you need to do is validate the application name and get the

          //application ID from the database.

          string PasswordHash = string.Empty;

          string PasswordSalt = string.Empty;

          string HashedPassword = string.Empty;

          string ApplicationID = GetApplicationID(applicationname);

          if (ApplicationID == null || ApplicationID == string.Empty)

          {

            //Cannot find the application name therefore the user cannot

            //Exist for this application

            return false;

          }

          //Next you need to retrieve the salt and hash from the database in order to

          //verify the password.

          Database db = DatabaseFactory.CreateDatabase();

          SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetPasswordWithFormat”);

          cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

          cmdSQL.Parameters.AddWithValue(“@Username”, username);

          cmdSQL.Parameters.AddWithValue(“@UpdateLastLoginActivityDate”, 0);

          cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, System.DateTime.Now);

          DataSet ds = null;

          cmdSQL.CommandType = CommandType.StoredProcedure;

          ds = db.ExecuteDataSet(cmdSQL);

          if (ds.Tables[0].Rows.Count > 0)

          {

            DataRow dr = ds.Tables[0].Rows[0];

            //The database does not name it’s returned rows.

            //The Salt is stored in the first field returned (Password field)

            //The Hash is stored in the 3rd field returned (PasswordSalt)

            PasswordHash = dr[0].ToString();

            PasswordSalt = dr[2].ToString();

          }

          else

          {

            //No rows were returned from the database therefore

            //User does not exist.

            return false;

          }

          //Now we hash the password supplied.

          HashedPassword = CreatePasswordHash(password, PasswordSalt);

          //Now simply compare the hashed password just generated with that stored

          //in the database.  If they match then username and password are correct

          //otherwise they are not.

          if (HashedPassword == PasswordHash)

          {

            return true;

          }

          else

          {

            return false;

          }

        }

      catch (Exception ex)

      {

        // TODO: Exception Handling routine

        return false;

      }

    }

 

    public bool UnlockUser(string username, string applicationname)

    {

      int ReturnValue = 0;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_UnlockUser”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@Username”, username);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      ReturnValue = Convert.ToInt32(db.ExecuteScalar(cmdSQL));

      if (ReturnValue == 0)

      {

        return true;

      }

      else

      {

        return false;

      }

    }

 

    public bool DeleteUser(string username, string applicationname, bool deleteallrelateddata)

    {

      int UserDeleted = 0;

      int ErrorCode = 0;

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Users_DeleteUser”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@UserName”, username);

      if (deleteallrelateddata == true)

      {

        cmdSQL.Parameters.AddWithValue(“@TablesToDeleteFrom”, 15);

      }

      else

      {

        cmdSQL.Parameters.AddWithValue(“@TablesToDeleteFrom”, 0);

      }

      SqlParameter output = new SqlParameter(“@NumTablesDeletedFrom”, SqlDbType.Int);

      output.Direction = ParameterDirection.Output;

      //SqlParameter errorcode = new SqlParameter(“@ErrorCode”, SqlDbType.Int);

      //errorcode.Direction = ParameterDirection.ReturnValue;

      cmdSQL.Parameters.Add(output);

      //cmdSQL.Parameters.Add(errorcode);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      db.ExecuteNonQuery(cmdSQL);

      UserDeleted = Convert.ToInt32(cmdSQL.Parameters[“@NumTablesDeletedFrom”].Value.ToString());

      //ErrorCode = Convert.ToInt32(cmdSQL.Parameters[“ErrorCode”].Value.ToString());

      if (UserDeleted == 0)

      {

        return false;

      }

      else

      {

        return true;

      }

    }

 

    public bool ChangePassword(string username, string oldpassword,

      string newpassword, string applicationname)

    {

      int ReturnCode = 0;

      string PasswordSalt = CreateSalt(5);

      string HashedPassword = CreatePasswordHash(newpassword, PasswordSalt);

      bool OldPasswordValidated = ValidateUser(username, oldpassword, applicationname);

      if(OldPasswordValidated == false)

      {

        return false;

      }

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_SetPassword”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@UserName”, username);

      cmdSQL.Parameters.AddWithValue(“@NewPassword”, HashedPassword);

      cmdSQL.Parameters.AddWithValue(“@PasswordSalt”, PasswordSalt);

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      cmdSQL.Parameters.AddWithValue(“@PasswordFormat”, MembershipPasswordFormat.Clear);

      SqlParameter returncode = new SqlParameter(“@ReturnCode”, SqlDbType.Int);

      returncode.Direction = ParameterDirection.ReturnValue;

      cmdSQL.CommandType = CommandType.StoredProcedure;

      db.ExecuteNonQuery(cmdSQL);

      ReturnCode = Convert.ToInt32(cmdSQL.Parameters[“@ReturnCode”].Value.ToString());

      if (ReturnCode == 0)

      {

        return true;

      }

      else

      {

        return false;

      }

    }

 

    public void UpdateUser(string applicationname, MembershipUser user)

    {

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_UpdateUser”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@UserName”, user.UserName);

      cmdSQL.Parameters.AddWithValue(“@Email”, user.Email);

      cmdSQL.Parameters.AddWithValue(“@Comment”, user.Comment);

      cmdSQL.Parameters.AddWithValue(“@IsApproved”, user.IsApproved);

      cmdSQL.Parameters.AddWithValue(“@LastLoginDate”, user.LastLoginDate);

      cmdSQL.Parameters.AddWithValue(“@LastActivityDate”, user.LastActivityDate);

      cmdSQL.Parameters.AddWithValue(“@UniqueEmail”, 1); //Requires Unique Email

      cmdSQL.Parameters.AddWithValue(“@CurrnetTimeUtc”, DateTime.Now);

      SqlParameter returncode = new SqlParameter(“@ReturnCode”, SqlDbType.Int);

      returncode.Direction = ParameterDirection.ReturnValue;

      cmdSQL.CommandType = CommandType.StoredProcedure;

      db.ExecuteNonQuery(cmdSQL);

      //Since we are returning void, do we really need to check the

      //return type?  Only course of action would be to throw a

      //exception which won’t work as this is a web service.

    }

 

    public int GetNumberOfUsersOnline(string applicationname)

    {

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetNumberOfUsersOnline”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@MinutesSinceLastInActive”, 20);  //value of IIS timeout

      cmdSQL.Parameters.AddWithValue(“@CurrentTimeUtc”, DateTime.Now);

      SqlParameter returnvalue = new SqlParameter(“@NumOnline”, SqlDbType.Int);

      returnvalue.Direction = ParameterDirection.ReturnValue;

      cmdSQL.Parameters.Add(returnvalue);

      db.ExecuteNonQuery(cmdSQL);

      return Convert.ToInt32(cmdSQL.Parameters[“@NumOnline”].Value.ToString());

    }

 

    public MembershipUserCollection FindUsersByName(string applicationname, string username)

    {

      DataSet ds = null;

      int RecordstoGo = 1;

      int TotalRecords = 0;

      int PageIndex = 0;

      MembershipUserCollection users = new MembershipUserCollection();

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_FindUsersByName”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@UserNameToMatch”, username);

      cmdSQL.Parameters.AddWithValue(“@PageIndex”, PageIndex);

      cmdSQL.Parameters.AddWithValue(“@PageSize”, 1000);  //Get 1000 users at a time

      SqlParameter NumberofUsers = new SqlParameter(“@TotalRecords”, SqlDbType.Int);

      NumberofUsers.Direction = ParameterDirection.ReturnValue;

      cmdSQL.Parameters.Add(NumberofUsers);

      ds = db.ExecuteDataSet(cmdSQL);

      TotalRecords = Convert.ToInt32(cmdSQL.Parameters[“TotalRecords”].Value.ToString());

      RecordstoGo = TotalRecords;

      do

      {

        if (ds.Tables[0].Rows.Count > 0)

        {

          for (int i = 0; i < ds.Tables[0].Rows.Count; i++)

          {

            DataRow dr = ds.Tables[0].Rows[i];

            MembershipUser user = PopulateMembershipUser(dr);

            users.Add(user);

          }

        }

        RecordstoGo -= 1000;

        if (RecordstoGo > 0)

        {

          PageIndex += 1;

          cmdSQL.Parameters[“@PageIndex”].Value = PageIndex;

          ds = db.ExecuteDataSet(cmdSQL);

        }

      } while (RecordstoGo > 0);

      return users;

 

    }

 

    public MembershipUserCollection GetAllUsers(string applicationname)

    {

      DataSet ds = null;

      int RecordstoGo = 1;

      int TotalRecords = 0;

      int PageIndex = 0;

      MembershipUserCollection users = new MembershipUserCollection();

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_GetAllUsers”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@PageIndex”, PageIndex);

      cmdSQL.Parameters.AddWithValue(“@PageSize”, 1000);  //Get 1000 users at a time

      SqlParameter NumberofUsers = new SqlParameter(“@TotalRecords”, SqlDbType.Int);

      NumberofUsers.Direction = ParameterDirection.ReturnValue;

      cmdSQL.Parameters.Add(NumberofUsers);

      ds = db.ExecuteDataSet(cmdSQL);

      TotalRecords = Convert.ToInt32(cmdSQL.Parameters[“TotalRecords”].Value.ToString());

      RecordstoGo = TotalRecords;

      do

      {

        if (ds.Tables[0].Rows.Count > 0)

        {

          for (int i = 0; i < ds.Tables[0].Rows.Count; i++)

          {

            DataRow dr = ds.Tables[0].Rows[i];

            MembershipUser user = PopulateMembershipUser(dr);

            users.Add(user);

          }

        }

        RecordstoGo -= 1000;

        if (RecordstoGo > 0)

        {

          PageIndex += 1;

          cmdSQL.Parameters[“@PageIndex”].Value = PageIndex;

          ds = db.ExecuteDataSet(cmdSQL);

        }

      } while (RecordstoGo > 0);

      return users;

    }

 

    public MembershipUserCollection FindUsersByEmail(string applicationname, string email)

    {

      DataSet ds = null;

      int RecordstoGo = 1;

      int TotalRecords = 0;

      int PageIndex = 0;

      MembershipUserCollection users = new MembershipUserCollection();

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Membership_FindUsersByEmail”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      cmdSQL.Parameters.AddWithValue(“@EmailToMatch”, email);

      cmdSQL.Parameters.AddWithValue(“@PageIndex”, PageIndex);

      cmdSQL.Parameters.AddWithValue(“@PageSize”, 1000);  //Get 1000 users at a time

      SqlParameter NumberofUsers = new SqlParameter(“@TotalRecords”, SqlDbType.Int);

      NumberofUsers.Direction = ParameterDirection.ReturnValue;

      cmdSQL.Parameters.Add(NumberofUsers);

      ds = db.ExecuteDataSet(cmdSQL);

      TotalRecords = Convert.ToInt32(cmdSQL.Parameters[“TotalRecords”].Value.ToString());

      RecordstoGo = TotalRecords;

      do

      {

        if (ds.Tables[0].Rows.Count > 0)

        {

          for (int i = 0; i < ds.Tables[0].Rows.Count; i++)

          {

            DataRow dr = ds.Tables[0].Rows[i];

            MembershipUser user = PopulateMembershipUser(dr);

            users.Add(user);

          }

        }

        RecordstoGo -= 1000;

        if (RecordstoGo > 0)

        {

          PageIndex += 1;

          cmdSQL.Parameters[“@PageIndex”].Value = PageIndex;

          ds = db.ExecuteDataSet(cmdSQL);

        }

      } while (RecordstoGo > 0);

      return users;

    }

 

    #region Overriden methods not used

    public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)

    {

      throw new NotSupportedException();

    }

 

    public override MembershipUser GetUser(string username, bool userIsOnline)

    {

      throw new NotSupportedException();

    }

 

    public override string GetUserNameByEmail(string email)

    {

      throw new NotSupportedException();

    }

 

    public override bool ValidateUser(string username, string password)

    {

      throw new NotSupportedException();

    }

 

    public override bool UnlockUser(string userName)

    {

      throw new NotSupportedException();

    }

 

    public override bool DeleteUser(string username, bool deleteAllRelatedData)

    {

      throw new NotSupportedException();

    }

 

    public override bool ChangePassword(string username, string oldPassword, string newPassword)

    {

      throw new NotSupportedException();

    }

 

    public override void UpdateUser(MembershipUser user)

    {

      throw new NotSupportedException();

    }

 

    public override int GetNumberOfUsersOnline()

    {

      throw new NotSupportedException();

    }

 

    public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)

    {

      throw new NotSupportedException();

    }

 

    public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)

    {

      throw new NotSupportedException();

    }

 

    public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)

    {

      throw new NotSupportedException();

    }

 

    public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)

    {

      throw new NotSupportedException();

    }

 

    public override string GetPassword(string username, string answer)

    {

      throw new NotSupportedException();

    }

 

    public override string ResetPassword(string username, string answer)

    {

      throw new NotSupportedException();

    }

 

    #endregion

    #region Helper Methods

    private string CreatePasswordHash(string password, string salt)

    {

      string SaltAndPassword = string.Concat(password, salt);

      //Now hash the salted password

      string HashedPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(SaltAndPassword, “SHA1”);

      return HashedPassword;

    }

 

    private string CreateSalt(int size)

    {

      //Generate a cryptographic random number using the cryptographic service provider

      System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();

      byte[] buffer = new byte[size];

      rng.GetBytes(buffer);

      return Convert.ToBase64String(buffer);

    }

 

    private string GetApplicationID(string applicationname)

    {

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Personalization_GetApplicationId”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      SqlParameter output = new SqlParameter(“@ApplicationID”, SqlDbType.UniqueIdentifier);

      output.Direction = ParameterDirection.Output;

      output.Value = null;

      cmdSQL.Parameters.Add(output);

      cmdSQL.CommandType = CommandType.StoredProcedure;

      db.ExecuteNonQuery(cmdSQL);

      return cmdSQL.Parameters[“@ApplicationID”].Value.ToString();

    }

 

    private string CreateApplication(string applicationname)

    {

      Database db = DatabaseFactory.CreateDatabase();

      SqlCommand cmdSQL = new SqlCommand(“aspnet_Applications_CreateApplication”);

      cmdSQL.Parameters.AddWithValue(“@ApplicationName”, applicationname);

      SqlParameter output = new SqlParameter(“@ApplicationID”, SqlDbType.UniqueIdentifier);

      output.Value = null;

      output.Direction = ParameterDirection.Output;

      cmdSQL.Parameters.Add(output);

      db.ExecuteNonQuery(cmdSQL);

      return cmdSQL.Parameters[“@ApplicationID”].Value.ToString();

    }

 

    private MembershipUser PopulateMembershipUser(DataRow dr)

    {

      MembershipUser user = new MembershipUser(m_ProviderName, dr[“UserName”].ToString(),

        new Guid(dr[“UserID”].ToString()), dr[“Email”].ToString(), dr[“PasswordQuestion”].ToString(),

        dr[“Comment”].ToString(), Convert.ToBoolean(dr[“IsApproved”]),

        Convert.ToBoolean(dr[“IsLockedOut”]), Convert.ToDateTime(dr[“CreateDate”]),

        Convert.ToDateTime(dr[“LastLoginDate”]), Convert.ToDateTime(dr[“LastActivityDate”]),

        Convert.ToDateTime(dr[“LastPasswordChangedDate”]), Convert.ToDateTime(dr[“LastLockoutDate”]));

      return user;

    }

    #endregion

  }

}

 

And that’s all there is to it. Creating your own membership providers really isn’t that hard and now we have something that we can use in conjunction with Live ID authentication.

 

In the last part of this series of articles I’ll explain how to put all of this together.

 

Windows Live ID – Part 3 – Putting it all together
Previously: Windows Live ID