Click here to Skip to main content
15,860,859 members
Articles / Web Development / ASP.NET

Improved ASP.NET Password Recovery

Rate me:
Please Sign up or sign in to vote.
4.82/5 (8 votes)
6 Dec 2009CPOL2 min read 96.7K   2.8K   33   6
This page will show you how to make an XHTML 1.1 strict password recovery form that will prompt for a username and email before continuing.

Introduction

The ASP.NET built in password recovery tool is pretty cool. With a few exceptions:

  1. It doesn't validate
  2. It only requires a username to continue the process, not a username/email match
  3. It sends a very basic email to whoever is recovering their password

This article will go over the process to customize the ASP.NET password recovery tool, and make it do the following things:

  1. Validate as XHTML 1.1
  2. Require a username/email combination in order to reset
  3. Send a customized email to the user whose password is being reset

Background

Anyone wishing to do this will need to be comfortable customizing ASP.NET controls. While this process isn't complicated, if you're new to ASP.NET, it may be a little confusing to you.

Using the code

First and foremost, you'll need to enable password retrieval in web.config. This will look like this:

XML
 <membership defaultProvider="CustomizedProvider">
    <providers>
        <clear />
        <add name="CustomizedProvider" 
           type="System.Web.Security.SqlMembershipProvider" 
           connectionStringName="YourConnectionstring" 
           applicationName="/APPName" 
           minRequiredPasswordLength="4" 
           minRequiredNonalphanumericCharacters="0" 
           enablePasswordReset="true" 
           requiresUniqueEmail="true" 
           maxInvalidPasswordAttempts="4" 
           passwordAttemptWindow="10" 
           requiresQuestionAndAnswer="true" />
    </providers>
</membership>

Note that the only attributes that must be set to the above values are "enablePasswordReset" and requiresQuestionAndAnswer.

Second, ensure the following section is also in your web.config:

XML
<system.net>
    <mailSettings>
        <smtp>
            <network defaultCredentials="false" 
               host="mail.YourHost.com" port="YOURPORT" 
               userName="EmailUsername@YourDomain.com" 
               password="YourPassword" />
        </smtp>
    </mailSettings>
</system.net>

This will allow your password recovery box to send emails.

Now for the password recovery control.

You'll notice that we have a literal named "PersonName". My web application stores the first and last names of registered users. I wanted to fill the second page of this form with their real name, as opposed to their username, which is the default.

ASP.NET
<asp:PasswordRecovery OnVerifyingUser="validateUserEmail" 
      SuccessText="Your password was successfully reset and emailed to you."
      QuestionFailureText="Incorrect answer. Please try again." 
      runat="server" ID="PWRecovery" 
      UserNameFailureText="Username not found.">
    <MailDefinition IsBodyHtml="true" BodyFileName="email.txt" 
           From="YourEmailAddress@YourDomain.com" 
           Subject="Password Reset" 
           Priority="High">
    </MailDefinition>
    <UserNameTemplate>
        <p>The steps below will allow you to have 
           a new password sent to the registered email address.</p>
        <dl>
            <dt>Username</dt>
            <dd>
                <asp:TextBox ID="Username" runat="server" />
            </dd>
            <dt>Email</dt>
            <dd>
                <asp:TextBox ValidationGroup="PWRecovery" 
                   runat="server" ID="EmailAddressTB">
                </asp:TextBox>
            </dd>
            <dt></dt>
            <dd>
                <asp:Button ID="submit" 
                   CausesValidation="true" 
                   ValidationGroup="PWRecovery" 
                   runat="server"
                   CommandName="Submit" 
                   Text="Submit" />
            </dd>
            <dt></dt>
            <dd>
                <p class="Error"><asp:Literal ID="ErrorLiteral" 
                         runat="server"></asp:Literal>
                </p>
            </dd>
        </dl>
    </UserNameTemplate>
    <QuestionTemplate>
        Hello
        <asp:Literal runat="server" ID="personname" />,
        <p>
            You must answer your recovery question 
            in order to have a new email sent to you.
        </p>
        <dl>
            <dt>Question:</dt>
            <dd>
                <asp:Literal runat="server" ID="Question" />
            </dd>
            <dt></dt>
            <dt>Answer:</dt>
            <dd>
                <asp:TextBox runat="server" ID="Answer" />
            </dd>
            <dt></dt>
            <dd>
                <asp:Button runat="server" ID="submit" 
                  Text="Submit" CommandName="submit" />
            </dd>
            <dt></dt>
            <dd>
                <p class="Error">
                    <asp:Literal ID="FailureText" runat="server">
    </asp:Literal>
    </p>
            </dd>
        </dl>
    </QuestionTemplate>
</asp:PasswordRecovery>

The code-behind for this file is relatively simple, with only a validation event.

You may have noticed that I created a cookie during the validation event. My web application stores a person's first and last name as a profile attribute, "FullName". I wanted this form to recognize them by name instead of by a username, and creating a cookie was a relatively easy way to do it.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;

public partial class retrievepw : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (IsPostBack)
        {
            Literal personname = 
              ((Literal)PWRecovery.QuestionTemplateContainer.FindControl("personname"));
            personname.Text = Request.Cookies["usernameCookie"].Value;
        }
    }
    protected void validateUserEmail(object sender, LoginCancelEventArgs e)
    {
        TextBox EmailAddressTB = 
            ((TextBox)PWRecovery.UserNameTemplateContainer.FindControl("EmailAddressTB"));
        
    Literal ErrorLiteral = 
      ((Literal)PWRecovery.UserNameTemplateContainer.FindControl("ErrorLiteral"));
        
    MembershipUser mu = Membership.GetUser(PWRecovery.UserName);
        
    if (mu != null) // The username exists
        {
            if (mu.Email.Equals(EmailAddressTB.Text)) // Their email matches
            {
                ProfileCommon newProfile = Profile.GetProfile(PWRecovery.UserName);
                HttpCookie appCookie = new HttpCookie("usernameCookie");
                appCookie.Value = newProfile.FullName;
                appCookie.Expires = DateTime.Now.AddMinutes(3);
                Response.Cookies.Add(appCookie);
            }
            else
            {
                e.Cancel = true;
                ErrorLiteral.Text = "Your username and password do not match";
            }
        }
        else
        {
            e.Cancel = true;
            ErrorLiteral.Text = "No such user found.";
        }
        
    }
}

Emails for the ASP.NET password recovery are sent using MailDefinition, which is correctly defined above. You may have noticed the "email.txt" value that was supplied. This corresponds to a .txt file on the web server.

This file's contents are below:

XML
<p>Hello,</p>
<p>You are receiving this email on behalf of YOUR WEBSITE 
          because you have requested a new password.</p>
<p>Your new password is <%Password%></p>
<p>We suggest that you login immediately and change your password.</p>
<p>Thanks,<br/>Your Website Team</p>

This email can use two variables from the form: <%Username%> and <%Password%>. You may use either as the occasion calls.

That's it! A password recovery box that will send a custom email, validate, and require a little more rigorous of an authentication step!

History

  • 12/6/2009 - Posted.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer Missouri State University
United States United States
I am a Computer Science student and web application programmer for Missouri State University. I have been doing web development for several years, and two years professionally.

Comments and Discussions

 
GeneralNice piece of work. :) Pin
kevinl3313-Feb-14 9:06
kevinl3313-Feb-14 9:06 
GeneralMy vote of 5 Pin
cblessinger27-Aug-13 9:23
cblessinger27-Aug-13 9:23 
QuestionWhat Happend with MDB databases? Pin
dafepi200429-Dec-10 17:32
dafepi200429-Dec-10 17:32 
QuestionProfileCommon Pin
Terppe9-Dec-09 1:02
Terppe9-Dec-09 1:02 
AnswerRe: ProfileCommon Pin
daegan9-Dec-09 13:17
daegan9-Dec-09 13:17 
GeneralRe: ProfileCommon Pin
Terppe14-Dec-09 5:25
Terppe14-Dec-09 5:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.