This registration
has a slick interface and user verification
NOTE* I do not cover encryption in this tutorial, but
I highly discourage storing plain-text passwords in your DB!
It is inevitable that every programmer will want to eventually make a site
which requires registration. Then you need to deal with spam accounts and all
that good stuff. This is part one of a series of tutorials, where I will show
you how to set up a registration process that requires a valid email address. I
will be building on the code provided here for the next tutorial and so on. In
the future, I will also include a login process I developed that will lock out
accounts after a certain amount of attempts, retrieve passwords, and all sorts
of other goodies. But before users can log in, they have to register.
I will be using a lot of other people's stuff in this example, such as jQuery,
jQuery extensions, Grid960 and so on as well as a lot of my own Extensions,
etc. With that said, this is going to have a lot of 'extras' included such as
Ajax functionality and some UI niceties to make it a quality interface - you
can feel free to cut these parts out, but I feel it will be nice for those that
want it. All of the code referenced is included.
Storage
The first step is setting up your user table in the
database. The most important thing to think of here is: what am I going to need
to collect? I am all about being as simple as possible, so I am going to
require two things: email and password - that is it. This is what I came up
with:
- userid - the primary key integer
- email - the user's email
- password - user's password
- guid - guid for verification
- created - date created; both for record keeping and to see if
it was not confirmed after a long time it can be removed
- confirmed - whether or not it is confirmed
- last try* - the last login
- number of failed logins* - number of failures
for lockout
The two starred items will not really be used in this too tutorial and are
optional if you do not want to prevent unlimited login attempts; though they
will be relevant in upcoming tutorials.
Here is the SQL to create my table for users:
CREATE TABLE dbo.users (
userid INT NOT NULL PRIMARY KEY IDENTITY,
email VARCHAR(100) NOT NULL UNIQUE,
password_ VARCHAR(30) NOT NULL,
guid_ UNIQUEIDENTIFIER NOT NULL UNIQUE,
created DATETIME NOT NULL,
confirmed BIT NOT NULL DEFAULT 0,
last_try DATETIME NOT NULL,
tries INT NOT NULL DEFAULT 0
);
Markup
Now that we have our table, go ahead and drag it into a dbml
- for this example, I will use one named db.dbml. Now we have our access
layer built, we can work on making a registration control; I will be making an
ascx registration control, so it can be plugged in aywhere I want to use it.
Since I am only collecting two bits of information, this will be a simple
control. Here is the markup, I will explain it afterwards:
register.ascx
<asp:UpdatePanel
ID="upContact" runat="server">
<ContentTemplate>
<div class="pad">
<asp:Panel ID="Report runat="server" />
<asp:Panel ID="pnlRegister"
runat="server" DefaultButton="registerSubmit">
<div
class="pad_sides">
<div>
<h4>
<asp:RequiredFieldValidator ID="rfvRegisterEmail" runat="server"
CssClass="validate" ValidationGroup="register"
ControlToValidate="registerEmail" ErrorMessage="required"
Display="Dynamic" />
<asp:RegularExpressionValidator
ID="regRegisterEmail" runat="server" ControlToValidate="registerEmail"
ErrorMessage="invalid email" CssClass="validate"
Display="Dynamic" ValidationGroup="register"
ValidationExpression="^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"
/>
email
</h4>
<asp:TextBox ID="registerEmail"
runat="server" CssClass="inputBig full"
/>
</div>
<div>
<h4>
<asp:RequiredFieldValidator ID="rfvRegisterPassword" runat="server"
CssClass="validate" ValidationGroup="register"
ControlToValidate="registerPassword" ErrorMessage="required"
Display="Dynamic" InitialValue="8 character minimum" />
<asp:RegularExpressionValidator
ID="regRegisterPassword" runat="server" CssClass="validate"
ValidationGroup="register"
ControlToValidate="registerPassword" ErrorMessage="must
be at least 8 characters" Display="Dynamic"
ValidationExpression="^.{8}.*$" />
password
</h4>
<asp:TextBox ID="registerPassword"
runat="server" CssClass="inputBig
full wm watermark" Text="8 character
minimum" />
</div>
<div
class="summary field">
<asp:LinkButton ID="registerSubmit" CssClass="button"
Text="submit" runat="server"
ValidationGroup="register" onclick="registerSubmit_Click" />
</div>
</div>
</asp:Panel>
</div>
</ContentTemplate>
</asp:UpdatePanel>
Ok,
there is a lot going on here, so I will go part by part.
First of all, you will notice that it is within a Asp.Net
UpdatePanel
which I have been trying to get away from for most things, but for such small
controls I have found that is is the best way to go about it: easy and fast.
Next you will see that I have added a
Panel with an ID of
"Report" - I use this as a standard in most applications as to where
to output my 'updates' to the user. This is
explained
here. The code for this is included in the
Extensions.cs file.
Next there is a good amount of validation going on.
- First I use RequiredFieldValidators for both
fields
- Then I added the RegularExpressionValidator for
emails
- Then I added the RegularExpressionValidator for
password length
- Finally you will notice that the password entry has a watermark
which is called via jQuery in the MasterPage
You might notice that I am not using a password field or asking for password
verification. This is something you might want to do, but for this example,
security is not really a concern, simplicity is; so I figure if you can see
your password, you wont screw it up. Also, since we will be adding a password
retrieval function, this won't be a big deal.
Backend
That is the markup, but now we have to go to the code so it
actually does something. Now what does this have to accomplish?
- Check if the Email has already been registered
- Create a new entry in the users table
- Send an email with a verification link
Not too much going on here, here is the code for accomplishing that, followed
by an explanation:
register.ascx.cs
using System;
public partial class controls_register
: System.Web.UI.
UserControl
{
protected void Page_Load(
object
sender,
EventArgs e)
{
if (
this.Attributes[
"in_page"] !=
null)
{
Utils.DuplicateValidators(
this);
}
}
protected void registerSubmit_Click(
object sender,
EventArgs e)
{
try
{
dbDataContext db
=
new dbDataContext();
if(
Users.DuplicateEmail(db, registerEmail.Text))
throw new
Exception(
"email already
registered");
Guid g =
Guid.NewGuid();
user u =
new user()
{
created =
DateTime.Now,
email = registerEmail.Text,
guid_ = g,
password_ =
registerPassword.Text,
last_try=
DateTime.Now
};
db.users.InsertOnSubmit(u);
db.SubmitChanges();
Email email =
new Email(registerEmail.Text,
Settings.Get(
"gm"),
"please
verify your email address",
"http://yoursite.com/confirm.aspx?guid="
+ g.ToString());
//we will get to this in the next tutorial
email.Send();
Report.Success(
"account
successfully created",
"please check your
email to verify your account");
pnlRegister.Visible =
false;
}
catch (
Exception ex)
{
Report.Error(ex);
}
}
}
The first thing that happens here is the check for if the
Attribute "in_page" is set. This is a bit of a sidebar as it just
takes care of duplicate validators if there is more than one of these controls
on the page, since I plan on showing how to use them both as a modal popup as
well as a standalone page I had to add this check; that way, if you are filling
out the popup instead of the form on the page it makes sure that it will not
fire the validation for the form you are not using, all it does is change the
validation group. The code is visible in the
Utils class if you are
curious about it. Don't really worry about this too much right now, as it will
be covered in an upcoming tutorial.
Next it checks if the email is a duplicate. This calls the
Users.cs
class, we will get to that next; just remember for now it returns true if it is
already in the system, false if not.
If it is new, a new user is then made and inserted into the DB via Linq-to-SQL.
An email is made and sent to the user with the link to the authorization page
(which will be coevered in the next tutorial). This is sent using a
simplified
Email class. The authorization is the guid which was produced - I will
cover the authorization page in the next part of the tutorial.
Then the user is notified of the status whether it is success or error using
the
panel
reporting extensions.
This is all pretty simple, all that is left is to explain what is going on in
the
Users.cs class which is also simple:
Users.cs
using System.Linq;
public static class Users
{
public static user
GetFromEmail(dbDataContext db, string
email)
{ return db.users.First(u =>
u.email.ToLower().Equals(email.ToLower())); }
public static bool DuplicateEmail(dbDataContext db, string email)
{
try
{
user temp =
GetFromEmail(db, email);
span class="var">return true;
}
catch { return
false; }
}
}
As you can see, this is just two basic LINQ queries, the
first one retrieving a user object based on an email string. And the
duplicate check which tries to find a user with the given email, if it
can, it will return true, meaning it is already in there, otherwise it spits
out a false.
And that is all there is to it so far. It is not yet a working system as the
user has not verified their identity, but we have accomplished the base of a
registration system:
- Collected the user's email address
- Collected their password
- Produced a unique Guid for verification
- Sent them an email to verify their identity
Now we have it all built, we just need to display everything and call the necessary
scripts. I am going to stick all of these pages within a masterpage which calls
the scripts in the Page_Load:
demo.master.cs
protected void Page_Load(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptInclude(typeof(demo), "jQuery",
ResolveUrl("~/js/jquery.js"));
//this will be used in the next tutorial
Page.ClientScript.RegisterClientScriptInclude(typeof(demo), "jQuery",
ResolveUrl("~/js/jqModal.js"));
}
Then just call the control (registered in the web.config
file) and the js in the markup:
register.aspx
<h2>Registration</h2>
<cc:register id="reg" runat="server"
/>
and call the watermark frorm jQuery:
demo.master
<script type="text/javascript"
language="javascript">
$().ready(function() {
swapValues = [];
$(".wm").each(function(i) {
swapValues[i] = $(this).val();
$(this).focus(function() {
if ($(this).val() == swapValues[i]) {
$(this).val("").removeClass("watermark")
}
})
})
});
</script>
Notice that I am calling the watermark in the masterpage.
This may seem strange, but this stops me from writing redundant code as this
will take care of *all* watermarks that I will put into this project due to the
versatiliy of jQuery.
All we have to do to complete this registration process is to verify the email
which will be the next part to this tutorial. I am also going to show how to
add this into a registration popup. The hard part is all finished.