Continued from: ASP.NET MVC 2 Custom Membership Provider Tutorial – Part 1
Just to quickly wrap up what we’ve done so far:
We created new project using ASP.NET MVC 2 Web Application to make use of the layout (login pages, master page, login controls) and speed up our development.
We created Custom Membership Provider and we implemented custom logic into the ValidateUser method to show how we can override Membership Provider’s methods.
In this part of the tutorial we’re going to implement a database, create UserRepository class which will contain data logic (to keep the provider as simple as possible and to leave Entity Framework model untouched so we can make modifications to the database without breaking things), and implement some of the Membership Provider’s basic methods.
First of all, I would like to say that this is first time I’m doing this too. Although I’m few steps ahead of the tutorial I’m open to suggestions on how the implementation can be improved (both, coding and the way it works).
While I’m at it, I’ll just quickly (sorry for all the delay) mention where I see this tutorial going in the next few parts.
We’re going to implement all default Membership Provider methods to make it tidy. Even if it means just returning hard-coded value like password format (for example: we don’t need the option between text/hashed passwords).
We will also implement email validation system that will request clicking auto-generated link in an email to register, reset password and change email address.
After we’ve done that, we will implement Role Provider and we will create admin section of the application.
So, coming back to this part of the tutorial, first we will need a database table to store all membership information. The table I’m proposing is basically a hybrid of aspnet_membership table from ASPNETDB database and Users table from Tank_Auth authentication library for CodeIgniter PHP framework (Check out the following link on Stack Overflow to find out more about Tank_Auth, it’s very informative – What Code Igniter authentication library is best? – Stack Overflow).
Let’s start by adding the database to our project. Right click App_Data folder, Add -> New Item…, Select SQL Server Database, call it CustomMembership.mdf and click Add.
In Database Explorer, right click on the Tables node and Add New Table. Create the following table:
Make UserId column a Primary Key and an Identity Column and save the table with the name Users.
Back in the Solution Explorer, right click Models folder Add -> New Item…, Select ADO.NET Entity Data Model, call it CustomMembership.edmx and click Add.
Select Generate from Database and click Next.
Change the connection name to CustomMembershipDB and click Next.
Select Tables, change Model Namespace to CustomMembership.Models and Finish the wizard.
Back in Solution Explorer open Web.config file and delete default ASPNETDB connection. We had to keep it there because Membership Provider needs connectionStringName parameter. Now, we have our DB connection which by the way Membership Provider is not going to use directly anyway, we can delete the ASPNETDB connection.
Change connectionStringName parameter value in Membership, Role and Profile provider nodes in Web.config file like on the screenshot below.
We can quickly run our application to confirm that we didn’t break anything… yet.
While we have the application running see what happens if we click Register link on the login page.
We could just hard-code MinRequiredPasswordLength propery into Custom Membership Provider if we wanted to.
What happens is that when the Membership Provider is initialized, the Initialize() method is called. It reads Web.config file and sets Membership Provider object’s properties. So, lets implement Initialize method in MyMembershipProvider class
First, lets add a reference to System.Collections.Specialized assembly:
using System.Collections.Specialized;
Next, create helper function which we will use to read config values.
//
// A helper function to retrieve config values from the configuration file.
//
private string GetConfigValue(string configValue, string defaultValue)
{
if (string.IsNullOrEmpty(configValue))
return defaultValue;
return configValue;
}
Next, lets add properties from Web.config file. We’re going to hard code some of the provider’s default properties. We will want unique email and hashed password and we don’t want to let users to retrieve their passwords (not even, using QuestionAndAnswer)
// // Properties from web.config, default all to False // private string _ApplicationName; private bool _EnablePasswordReset; private bool _EnablePasswordRetrieval = false; private bool _RequiresQuestionAndAnswer = false; private bool _RequiresUniqueEmail = true; private int _MaxInvalidPasswordAttempts; private int _PasswordAttemptWindow; private int _MinRequiredPasswordLength; private int _MinRequiredNonalphanumericCharacters; private string _PasswordStrengthRegularExpression; private MembershipPasswordFormat _PasswordFormat = MembershipPasswordFormat.Hashed;
And finally, lets add Initialize method.
public override void Initialize(string name, NameValueCollection config)
{
if (config == null)
throw new ArgumentNullException("config");
if (name == null || name.Length == 0)
name = "CustomMembershipProvider";
if (String.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", "Custom Membership Provider");
}
base.Initialize(name, config);
_ApplicationName = GetConfigValue(config["applicationName"],
System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
_MaxInvalidPasswordAttempts = Convert.ToInt32(
GetConfigValue(config["maxInvalidPasswordAttempts"], "5"));
_PasswordAttemptWindow = Convert.ToInt32(
GetConfigValue(config["passwordAttemptWindow"], "10"));
_MinRequiredNonalphanumericCharacters = Convert.ToInt32(
GetConfigValue(config["minRequiredNonalphanumericCharacters"], "1"));
_MinRequiredPasswordLength = Convert.ToInt32(
GetConfigValue(config["minRequiredPasswordLength"], "6"));
_EnablePasswordReset = Convert.ToBoolean(
GetConfigValue(config["enablePasswordReset"], "true"));
_PasswordStrengthRegularExpression = Convert.ToString(
GetConfigValue(config["passwordStrengthRegularExpression"], ""));
}
I think the function is pretty much self explanatory. If you remove all unnecessary line breaks it will look much tidier.
We can edit Web.config‘s membership node and delete all the hardcoded parameters.
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear />
<add name="CustomMembershipProvider"
type="MyMembershipProvider"
connectionStringName="CustomMembershipDB"
enablePasswordReset="true"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="6"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10"
applicationName="/" />
</providers>
</membership>
Next, let’s implement the properties fully into our Custom Membership Provider.
Open MyMembershipProvider file and starting from the top edit properties and methods as below:
public override string ApplicationName
{
get { return _ApplicationName; }
set { _ApplicationName = value; }
}
Because we will not implement password reset using security question and answer we can change:
public override bool ChangePasswordQuestionAndAnswer(string username,
string password,
string newPasswordQuestion,
string newPasswordAnswer)
{
return false;
}
public override bool EnablePasswordReset
{
get { return _EnablePasswordReset; }
}
public override bool EnablePasswordRetrieval
{
get { return _EnablePasswordRetrieval; }
}
public override int MaxInvalidPasswordAttempts
{
get { return _MaxInvalidPasswordAttempts; }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { return _MinRequiredNonalphanumericCharacters; }
}
public override int MinRequiredPasswordLength
{
get { return _MinRequiredPasswordLength; }
}
public override int PasswordAttemptWindow
{
get { return _PasswordAttemptWindow; }
}
public override MembershipPasswordFormat PasswordFormat
{
get { return _PasswordFormat; }
}
public override string PasswordStrengthRegularExpression
{
get { return _PasswordStrengthRegularExpression; }
}
public override bool RequiresQuestionAndAnswer
{
get { return _RequiresQuestionAndAnswer; }
}
public override bool RequiresUniqueEmail
{
get { return _RequiresUniqueEmail; }
}
At this point, we can run the application and go to Log In -> Register page and we will see:
with minimum password length being taken from Web.config file.
In case I’ve made some typos while copy and pasting the code here you can download MyMembershipProvider.cs file up to this point of the tutorial:
If we try to register new account we will get an error about CreateUser method not being implemented.
Before we implement CreateUser method we need to implement 2 other methods which are used by CreateUser method: GetUser and GetUsernameByEmail.
We need them both to check for existing usernames and email addresses in our database.
Let’s start by creating UserRepository class.
Right click Models folder, Add -> Class…, call it UserRepository and click Add.
In UserRepository class, add reference to System.Web.Security assembly:
using System.Web.Security;
We’re going to implement 3 methods at this stage:
CreateUser:
public MembershipUser CreateUser(string username, string password, string email)
{
using (CustomMembershipDB db = new CustomMembershipDB())
{
User user = new User();
user.UserName = username;
user.Email = email;
user.Password = password;
user.PasswordSalt = "1234";
user.CreatedDate = DateTime.Now;
user.IsActivated = false;
user.IsLockedOut = false;
user.LastLockedOutDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
db.AddToUsers(user);
db.SaveChanges();
return GetUser(username);
}
}
We can see that our UserRepository CreateUser method uses Entity Data Model (CustomMembershipDB) we created earlier. We will come back to hasing and salting the password in the next part of the tutorial.
It creates User object, saves it to DB and returns user object of MembershipUser type.
In case you’re wondering why we assign DateTime.Now values to LastLockedOutDate and LastLoginDate. This is because in default MembershipUser type these properties are not Nullable, which means that they can’t store NULL values. There is a way to implement DateTime? nullable type but for the simplicity we’ll just populate the fields with current time on user creation.
GetUserNameByEmail:
public string GetUserNameByEmail(string email)
{
using (CustomMembershipDB db = new CustomMembershipDB())
{
var result = from u in db.Users where (u.Email == email) select u;
if (result.Count() != 0)
{
var dbuser = result.FirstOrDefault();
return dbuser.UserName;
}
else
{
return "";
}
}
}
GetUser:
public MembershipUser GetUser(string username)
{
using (CustomMembershipDB db = new CustomMembershipDB())
{
var result = from u in db.Users where (u.UserName == username) select u;
if (result.Count() != 0)
{
var dbuser = result.FirstOrDefault();
string _username = dbuser.UserName;
int _providerUserKey = dbuser.UserId;
string _email = dbuser.Email;
string _passwordQuestion = "";
string _comment = dbuser.Comments;
bool _isApproved = dbuser.IsActivated;
bool _isLockedOut = dbuser.IsLockedOut;
DateTime _creationDate = dbuser.CreatedDate;
DateTime _lastLoginDate = dbuser.LastLoginDate;
DateTime _lastActivityDate = DateTime.Now;
DateTime _lastPasswordChangedDate = DateTime.Now;
DateTime _lastLockedOutDate = dbuser.LastLockedOutDate;
MembershipUser user = new MembershipUser("CustomMembershipProvider",
_username,
_providerUserKey,
_email,
_passwordQuestion,
_comment,
_isApproved,
_isLockedOut,
_creationDate,
_lastLoginDate,
_lastActivityDate,
_lastPasswordChangedDate,
_lastLockedOutDate);
return user;
}
else
{
return null;
}
}
}
Again, we can see that some of the properties get DateTime.Now value, and again this is because MembershipUser constructor needs the values and can’t accept NULLs.
So basically what the method does is; It connects to database and reads the user data; It then generates new MembershipUser object with properties from database and finally it returns the object.
UserRepository class can be downloaded here:
Now, let’s go back to MyMembershipProvider.cs, add the reference to CustomMembership.Models namespace
using CustomMembership.Models;
and implement the following methods:
GetUserNameByEmail:
public override string GetUserNameByEmail(string email)
{
UserRepository _user = new UserRepository();
return _user.GetUserNameByEmail(email);
}
GetUser(string username, bool userIsOnline) - (not the one with userProviderKey argument):
public override MembershipUser GetUser(string username, bool userIsOnline)
{
UserRepository _user = new UserRepository();
return _user.GetUser(username);
}
and finally CreateUser itself:
public override MembershipUser CreateUser(string username,
string password,
string email,
string passwordQuestion,
string passwordAnswer,
bool isApproved,
object providerUserKey,
out MembershipCreateStatus status)
{
ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username,
password,
true);
OnValidatingPassword(args);
if (args.Cancel)
{
status = MembershipCreateStatus.InvalidPassword;
return null;
}
if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
{
status = MembershipCreateStatus.DuplicateEmail;
return null;
}
MembershipUser u = GetUser(username, false);
if (u == null)
{
UserRepository _user = new UserRepository();
_user.CreateUser(username, password, email);
status = MembershipCreateStatus.Success;
return GetUser(username, false);
}
else
{
status = MembershipCreateStatus.DuplicateUserName;
}
return null;
}
MyMembershipProvider class up to this point in the tutorial can be downloaded here:
At this point, we should be able to register using the registration form of our application (Log In -> Regsiter – or /Account/Register link) and check directly in the database if the account has been created.
It would also be easy enough to modify ValidateUser method to validate against the database but because there is still few things we need to do during user creation process (hash the passwords and generate salt for example) we will leave it for the next part of the tutorial.
Continue to:
ASP.NET MVC 2 Custom Membership Provider Tutorial – Part 3











can’t wait for Part III …
Good job man… This is gonna be the greatest custom membership provider tutorial on the net… Can’t wait for the upcoming parts….
Looking forward to the next part!
Great stuff man, roll on part three!
project error !
What is the error you’re getting?
error code ;
db.SaveChanges();
An error occurred while updating the entries. See the inner exception for details.
What is the inner exception?
To see it, when the exception is thrown, in Visual Web Developer click on View Details… against the exception.
In View Detail windows open the node and there is an inner exception entry there.
Please post it.
thanks a lot.
error corrected.
I am following your article.
Thank you for this great tutorial. I am getting a error message… The type or namespace name ‘CustomMembershipDB’ could not be found (are you missing a using directive or an assembly reference?. What have I left out?
How did you fix this? I’ve been stuck with this undefined “CustomMembershipDb” for a couple of days!
Excuse me, I meant “the namespace cannot be found when I added
using CustomMembership.Models;
to MyMembershipProvider.cs
the fix is this:
in MyMembershipProvider.cs, replace the offending line:
using CustomMembership.Models;
with
using .Models;
inserting my/your specific project name above
this is necessary for me, perhaps because I am running MVC 3?
Sorry, one more clarification:
the fix was necessary most likely because the author’s project name is CustomMembership, whereas that for me is merely the name for the custom membership data entity part of my project.
It was my typo error.
Would be nice of you to provide that .sql script to create the table
In the Initialize() method:
Not sure I understand why there is the check-removal-adding of the “description”, since the next call to base.Initialize(name,config) will remove “description” from the config.
Should base.Initialize(name,config) be called ? If so, where exactly in the overridden Initialize() ?
Very good job here! If i could suggest you to write the database table structure by code – it’s one-time job but it is possible to use this structure in the future.
Here is the code:
create table Users
(UserId int identity(1,1) not null primary key,
UserName nvarchar(30) not null,
Email nvarchar(100) not null,
Password nvarchar(128) not null,
PasswordSalt nvarchar(128) not null,
Comments nvarchar(256),
CreatedDate datetime not null,
LastModifiedDate datetime,
LastLoginDate datetime not null,
LastLoginIp nvarchar(40),
IsActivated bit not null,
IsLockedOut bit not null,
LastLockedOutDate datetime not null,
LastLockedOutReason nvarchar(256),
NewPasswordKey nvarchar(128),
NewPasswordRequested datetime,
NewEmail nvarchar(100),
NewEmailKey nvarchar(128),
NewEmailRequested datetime,
);
Cheers! ;]
hey, thank you for this excellent post. But i have some trouble with ‘CustomMembershipDB’, How does VS knows this class, where is its implementation ? are you missing a using directive or an assembly reference?
Thank you
Hey. CustomMembershipDB is a class that inherits from ObjectContext. You can find it code behind CustomMembership.edmx. It initializes connection to “CustomMembershipDB” connection string target.
ok thank you, but why i got this message so” The type or namespace name ‘CustomMembershipDB’ could not be found”
What could be the solution?
thanks
Very good job!!!
i have found below error.fyi i have follow ur exercise from part1 exactly.but stuck on this. please advice.
Error 1 Type ‘MyMembershipProvider’ already defines a member called ‘GetUser’ with the same parameter types C:\Users\Zira\documents\visual studio 2010\Projects\MvcApplication_test2\MvcApplication_test2\App_Data\MyMembershipProvider.cs 150 36 MvcApplication_test2
in addtion, noticed that this function is with same name and parameters.
appreciate you can advice as this 2 function i have follow ur e.g accordingly.
public override MembershipUser GetUser(string username, bool userIsOnline)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
UserRepository _user = new UserRepository();
return _user.GetUser(username);
}
[...] to Vote[FriendFeed] ASP.NET MVC 2 Custom Membership Provider Tutorial – Part 2 « The Integrity (6/19/201…Sunday, June 19, 2011 from [...]
Great tutorial! But what is the reasoning for moving the values from the web.config and hard coding into the application itself?
Thanks
I have been following this tutorial. I think it is terrific. I would like to see exactly what the web.config file looks like at this point or maybe in Part III, when done. I am uncertain which elements need to remain in place as place holders in the config file.
Thanks a bunch!!! Great Tutorial!
hi,
this is only inserting data into Users table not into ASPNETDB database why?
it should also have to update that membership table too?
HOW CAN I ADD EXTRA FIELDS LIKE FIRST NAME , LAST NAME ETC WHEN I CREATE AN NEW USER??
just curious why in “public MembershipUser GetUser(string username)” you create variables to store the values before assigning them to “MembershipUser user = new MembershipUser.”
To me since there is no other error checking or anything like that being done it seems like unnecessary coding. Am I missing something? Is it there for the sole reason of readability on the datetime values?
Don’t get me wrong so far this is by far the clearest example of how to make a custom membership provider I’ve found this is just something I saw and didn’t see a reason behind it. Again, thanks so much for the article!
I’d also suggest a follow up for MVC3 or just add in the differences through the tutorial when there are some (if there are some).