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

ASP.NET MVC - reCAPTCHA and Email Confirmation

By , 25 Mar 2013
 

Introduction

ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that enable a clean separation of concerns and that gives you full control over markup for enjoyable, agile development.

ASP.NET MVC includes many features that enable fast, TDD-friendly development for creating sophisticated applications that use the latest web standards.

You can also choose an existing platform based on ASP.NET MVC from Microsoft Gallery, from:

  • Blogs
  • CMS
  • eCommerce
  • Forums
  • Galleries
  • Wiki
  • And more

But one thing that you probably need to add to any Web Application is BOTS Protection and Email Integrity.

So how can you prevent abuse from "bots", automated programs usually written to generate spam, and how to make sure the user has given you his real email address when registering on your Web Application?

The answer is: reCAPTCHA and Email Confirmation

reCAPTCHA

ASPNETMVCSecurity/1.png

A CAPTCHA is a program that can tell whether its user is a human or a computer. You've probably seen them — colorful images with distorted text at the bottom of Web registration forms. CAPTCHAs are used by many websites to prevent abuse from "bots," or automated programs usually written to generate spam. No computer program can read distorted text as well as humans can, so bots cannot navigate sites protected by CAPTCHAs.

Step 1: Download and Get reCAPTCHA Keys

First go to the reCAPTCHA site and register for a unique key, then Download reCAPTCHA .NET Library.

Save your Public and Private keys safely.

ASPNETMVCSecurity/2.png

Step 2: Create ASP.NET MVC 3 Web Application

Now, let’s start a new ASP.NET MVC 3 Web Application Project in Visual Studio 2010.

ASPNETMVCSecurity/3.png

Choose to create from “Internet Application” template

ASPNETMVCSecurity/4.png

Step 3: Recaptcha

Add the Recaptcha DLL as a reference to your project.

ASPNETMVCSecurity/5.png

Modify site Web.config with new keys under the appSettings section and add the keys we received before.

  • ReCaptchaPrivateKey
  • ReCaptchaPublicKey

ASPNETMVCSecurity/6.png

Step 4: Change Registration

Open the Controllers folder, open the AccountController file, and locate the Register method, add the new attribute:

[RecaptchaControlMvc.CaptchaValidatorAttribute] 

ASPNETMVCSecurity/7.png

And add the reCAPTCHA logic to your registration code:

[HttpPost]
[RecaptchaControlMvc.CaptchaValidatorAttribute]
public ActionResult Register(RegisterModel model, bool captchaValid)
{
    if (!captchaValid)
    {
        ModelState.AddModelError("", "You did not type the verification 
                                    word correctly. Please try again.");
    }
    else
    {
        if (ModelState.IsValid)
        {
            // Attempt to register the user
            MembershipCreateStatus createStatus;
            Membership.CreateUser(model.UserName, model.Password, 
                                  model.Email, null, null, true, null, 
                                  out createStatus);
 
            if (createStatus == MembershipCreateStatus.Success)
            {
                FormsAuthentication.SetAuthCookie(model.UserName, false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                ModelState.AddModelError("",
                               ErrorCodeToString(createStatus));
            }
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}

Now place the following command in the Register.cshtml file:

@Html.Raw(Html.GenerateCaptcha()) 

ASPNETMVCSecurity/8.png

Run your web site and enter the registration page, now you have to enter captcha before compiling the registration.

ASPNETMVCSecurity/9.png

Email Confirmation

In order to make sure the user has gave you his real email you need to use Email Confirmation mechanism to your site.

ASP MVC is doing some of the work for us, when a user is register to your site is assign with a unique ID (GUID) property called - ProviderUserKey

I’ll use this value to verify the account.

Step 1: Modify Web.Config with SMTP Settings

Before we get started with the Email Confirmation implementation we need to define our email settings, to do so I’ve used my Gmail account (just for this test) and add those values in the web.config just below the configuration tag.

<configuration>
  <system.net>
    <mailSettings>
      <smtp deliveryMethod="Network">
        <network host="smtp.gmail.com" port="587" 
                                       userName="[YourName@Gmail.com]" 
                                       password="[Your Password]" />
      </smtp>
    </mailSettings>
  </system.net> 

Step 2: Add Confirmation View

After the user has register we want to redirect him to a confirmation page and tell him to check his email for the confirmation link, so:

Add a new View called – Confirmation

 @{
    ViewBag.Title = "Email Confirmation";
}
<h2>
    Confirmation</h2>
<p>
    Thank you for registering. Please check your email for a confirmation
    request with a link that will confirm your account. Once you click the
    link, your registration will be complete.</p> 

In the AccountController add the result for confirmation.

public ActionResult Confirmation()
{
    return View();
} 

Step 3: Send Mail

Before changing the registration we need to create the email functionality, this confirmation email will contain the ProviderUserKey and a direct link to our site.

The SendConfirmationEmail method will receive the UserName, and using Membership.GetUser we’ll be able to get all the information on that user and of course the ProviderUserKey.

I’ve built the confirmation URL using HttpContext and combined the User Guid ID and the site.

 var verifyUrl = HttpContext.Current.Request.Url.GetLeftPart 
(UriPartial.Authority) + "/Account/Verify/" + confirmationGuid; 
public class EmailManager
{
    private const string EmailFrom = "noreplay@gmail.com";
    public static void SendConfirmationEmail(string userName)
    {
        var user = Membership.GetUser(userName.ToString());
        var confirmationGuid = user.ProviderUserKey.ToString();
        var verifyUrl = HttpContext.Current.Request.Url.GetLeftPart
           (UriPartial.Authority) + "/Account/Verify/" + confirmationGuid;
 
        using (var client = new SmtpClient())
        {
            using (var message = new MailMessage(EmailFrom, user.Email))
            {
                message.Subject = "Please Verify your Account";
                message.Body = "<html><head><meta content=\"text/html; 
                charset=utf-8\" /></head><body><p>Dear " + user.UserName +
                   ", </p><p>To verify your account, please click the following link:</p>"
                   + "<p><a href=\"" + verifyUrl + "\" target=\"_blank\">" + verifyUrl + "
                   +"</a></p><div>Best regards,</div><div>Someone</div><p>Do not forward "
                   +"this email. The verify link is private.</p></body></html>";
 
                message.IsBodyHtml = true;
                    
                client.EnableSsl = true;
                client.Send(message);
            };
        };
    }
} 

Step 4: Change Registration

Now we need to change the Registration functionality. The main thing is when creating a new user we need to change the user state to Not Approve and instead of performing login, we need to send him an email using our EmailManager and show him the confirmation page.

[HttpPost]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        MembershipCreateStatus createStatus;
        //Make sure the user is not approve at this point!!!
        Membership.CreateUser(model.UserName, model.Password, model.Email,
        null, null, false, null, out createStatus);
 
        if (createStatus == MembershipCreateStatus.Success)
        {
            EmailManager.SendConfirmationEmail(model.UserName);
            return RedirectToAction("Confirmation", "Account");
        }
        else
        {
            ModelState.AddModelError("", ErrorCodeToString(createStatus));
        }
    }
 
    // If we got this far, something failed, redisplay form
    return View(model);
} 

Step 5: Add Verify

In AccountController I’ve added a Verify ActionResult. Verify will receive the User ID, then we’ll check for some bad IDs, if the user is already approved SignOut the user and redirect to the LogOn page, if the user is not approved, we need to change IsApprove to true and call UpdateUser to update the database with the new changes, also we will do the login for the user and redirect him to the home page.

public ActionResult Verify(string id)
{
    if (string.IsNullOrEmpty(id) || (!Regex.IsMatch(id, @"[0-9a-f]{8}\-
                                     ([0-9a-f]{4}\-){3}[0-9a-f]{12}")))
    {
        ViewBag.Msg = "Not Good!!!";
        return View();
    }
 
    else
    {
        var user = Membership.GetUser(new Guid(id));
 
        if (!user.IsApproved)
        {
            user.IsApproved = true;
            Membership.UpdateUser(user);
            FormsAuthentication.SetAuthCookie(user.UserName, false);
            return RedirectToAction("Index", "Home");
        }
        else
        {
            FormsAuthentication.SignOut();
            ViewBag.Msg = "Account Already Approved";
            return RedirectToAction("LogOn");
        }
    }
} 

Links

Enjoy!

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Shai Raiten
Architect Sela
Israel Israel
Member
Shai Raiten is VS ALM MVP, currently working for Sela Group as a ALM senior consultant and trainer specializes in Microsoft technologies especially Team System and .NET technology. He is currently consulting in various enterprises in Israel, planning and analysis Load and performance problems using Team System, building Team System customizations and adjusts ALM processes for enterprises. Shai is known as one of the top Team System experts in Israel. He conducts lectures and workshops for developers\QA and enterprises who want to specialize in Team System.
 
My Blog: http://blogs.microsoft.co.il/blogs/shair/

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membercsharpbd11 Apr '13 - 22:14 
Nice Article!!!
GeneralRe: My vote of 5mvpShai Raiten11 Apr '13 - 23:26 
Thanks!
Shai Raiten
  • Visual Studio ALM MVP 2009-2012
  • Codeproject MVP 2012
  • My Blog

SuggestionI don't have the Html.GenerateCaptcha() methodmembersinmas20 Nov '12 - 7:47 
I don't have the Html.GenerateCaptcha() method on the HTML helper used in the view:
 
@Html.Raw(Html.GenerateCaptcha())
 
In my case i built an HTLMExtension static class, inside my model file that renders the captcha
 
public static class HtmlExtensions
    {
 
        public static MvcHtmlString GenerateCaptcha()
        {
            string publicKey = ConfigurationManager.AppSettings["ReCaptchaPublicKey"];
            string privateKey = ConfigurationManager.AppSettings["ReCaptchaPrivateKey"];
            var captchaControl = new Recaptcha.RecaptchaControl
            {
                ID = "recaptcha",
                Theme = "red",
                PublicKey = publicKey,
                PrivateKey = privateKey
            };
 
            var htmlWriter = new HtmlTextWriter(new StringWriter());
 
            captchaControl.RenderControl(htmlWriter);
 
            return new MvcHtmlString(htmlWriter.InnerWriter.ToString());
        }
 
    }
 

Then I do a reference to the namestapace that contains the class on the view adding this line at the beggining
 
@using Telefe.UserRegistration.FrontEnd.Models 
 
So to render the reCaptcha just use:
 
@HtmlExtensions.GenerateCaptcha()
 
just one way to do the work, regards
GeneralMy vote of 5memberMallikarjun4u29 Oct '12 - 5:55 
nice
GeneralRe: My vote of 5mvpShai Raiten11 Apr '13 - 23:26 
Thanks!
Shai Raiten
  • Visual Studio ALM MVP 2009-2012
  • Codeproject MVP 2012
  • My Blog

GeneralMy Vote 5memberGSerjo28 Jul '12 - 6:05 
Excellent and very useful, thanks!
GeneralRe: My Vote 5mvpShai Raiten11 Apr '13 - 23:25 
Thanks!
Shai Raiten
  • Visual Studio ALM MVP 2009-2012
  • Codeproject MVP 2012
  • My Blog

QuestionAbout articlemember006ashish13 Jun '12 - 20:05 
Hi I am getting error that, can not find defination for   GenerateRecapcha() ;
please tell me what I had done wrong
AnswerRe: About articlememberTomasz Sikora1 Oct '12 - 8:18 
I had the same problem.
You have to write your own extension method like this:
 
public static class MyExtensions
    {
        public static string GenerateCaptcha(this HtmlHelper htmlHelper)
        {
            return Recaptcha.RecaptchaControlMvc.GenerateCaptcha(htmlHelper);
        }
    }

GeneralMy vote of 5memberJF201521 May '12 - 23:00 
Very nice article and really useful. Thanks.
QuestionJust tried on asp.net 4.5 Visual Studio 11 Beta - works amazing! Vote 5memberMember 80030639 May '12 - 4:08 
.
QuestionPLEASE HELP !memberYasser Youssef15 Apr '12 - 23:47 
thank you so much for the post.
 
I followed your instructions to the letter. but it's not working in some cases!
 
Now, the code works GREAT if I am NOT using SSL. and it fails with exception if I am using SSL.
 
I am using the self signed SSL on IIS.
 
Can you please tell me how to solve this?
QuestionActionResult Verify(string id) -- id is nullmemberjeff00seattle29 Mar '12 - 12:07 
Really nice article.
 
I have implemented your article onto my site,
 
My confirmation email link is http://localhost:4515/Account/Verify/f6af8b26-11d1-48a5-af9d-c8e8d4008945
 
method ActionResult Verify(string id) is getting called, but string id parameter is null.
 
Is there a page configuration that I need to do?
 
Thanks
 
Jeff in Seattle
Jeff in Seattle
Interests:
Dating my spouse, Playing with my daughter, and Camping in my SportsMobile.

AnswerRe: ActionResult Verify(string id) -- requires [HttpGet]memberjeff00seattle29 Mar '12 - 12:31 
Figured it out...
 
[HttpGet]
public ActionResult Verify(string id)
Jeff in Seattle
Interests:
Dating my spouse, Playing with my daughter, and Camping in my SportsMobile.

QuestionNice articememberGamersWanted5 Mar '12 - 9:01 
Nice article Shai. Did you look at http://www.nucaptcha.com/ as well as reCAPTCHA?
 
It's pretty nice and has a lot more features.

AnswerRe: Nice articemvpShai Raiten22 Mar '12 - 21:35 
Thanks but it's not free product to use, so I demo something that everyone can use without paying.
Shai Raiten
  • Visual Studio ALM MVP 2009-2011
  • Codeproject MVP 2012
  • My Blog

QuestionMy vote of 5memberGanesanSenthilvel25 Feb '12 - 17:23 
My vote of 5
AnswerRe: My vote of 5mvpShai Raiten22 Mar '12 - 21:35 
Thanks
Shai Raiten
  • Visual Studio ALM MVP 2009-2011
  • Codeproject MVP 2012
  • My Blog

GeneralMy vote of 5mentorMd. Marufuzzaman7 Feb '12 - 19:59 
Very nice effort...
GeneralRe: My vote of 5mvpShai Raiten22 Mar '12 - 21:35 
Thanks
Shai Raiten
  • Visual Studio ALM MVP 2009-2011
  • Codeproject MVP 2012
  • My Blog

Generalmy vote of 5memberUday P.Singh6 Feb '12 - 6:30 
good job! Smile | :)
GeneralRe: my vote of 5mvpShai Raiten6 Feb '12 - 6:58 
Thank You!
Shai Raiten
  • Visual Studio ALM MVP 2009-2011
  • Codeproject MVP 2012
  • My Blog

GeneralUsefulmemberDean Oliver25 Jan '12 - 5:19 
Very useful to know. Since most sites with some sort of registration pages etc can't do without.
GeneralRe: UsefulmvpShai Raiten6 Feb '12 - 6:58 
Thank You!
Shai Raiten
  • Visual Studio ALM MVP 2009-2011
  • Codeproject MVP 2012
  • My Blog

GeneralMy vote of 5memberscott.leckie19 Jan '12 - 2:01 
Really good. Simple, clear and informative - thanks!

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 25 Mar 2013
Article Copyright 2012 by Shai Raiten
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid