Click here to Skip to main content
Click here to Skip to main content

Improved ASP.NET Password Recovery

, 6 Dec 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

 <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:

<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: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.

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:

<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)

Share

About the Author

daegan
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. :) Pinmemberzeus3313-Feb-14 9:06 
GeneralMy vote of 5 Pinmembercblessinger27-Aug-13 9:23 
QuestionWhat Happend with MDB databases? Pinmemberdafepi200429-Dec-10 17:32 
QuestionProfileCommon PinmemberTerppe9-Dec-09 1:02 
AnswerRe: ProfileCommon Pinmemberdaegan9-Dec-09 13:17 
GeneralRe: ProfileCommon PinmemberTerppe14-Dec-09 5:25 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.141022.2 | Last Updated 6 Dec 2009
Article Copyright 2009 by daegan
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid