3
\$\begingroup\$

This entire class came out of a chat discussion, and I'm curious on how it looks. (This is like literally 30 minutes of development time.)

The idea is to allow very easy, quick implementations of Google's reCAPTCHA ("I am not a robot") checkbox CAPTCHA algorithm. It only requires minor work from the implementor to make it work properly, and that was the point.

Warning: Minimal implementation effort required.

public class ReCaptchaValidator
{
    private const string _HeadScriptInclude = "<script src='https://www.google.com/recaptcha/api.js'></script>";
    private const string _ReCaptchaLocationInclude = "<div class=\"g-recaptcha %EXTRACLASSES%\" data-sitekey=\"%SITEKEY%\"></div>";

    private readonly string _ReCaptchaSecret;
    private readonly string _ReCaptchaSiteKey;

    /// <summary>
    /// Returns the script to be included in the <code>&lt;head&gt;</code> of the page.
    /// </summary>
    public string HeadScriptInclude { get { return _HeadScriptInclude; } }

    /// <summary>
    /// Use this to get or set any extra classes that should be added to the <code>&lt;div&gt;</code> that is created by the <see cref="ReCaptchaLocationInclude"/>.
    /// </summary>
    public List<string> ExtraClasses { get; set; }

    /// <summary>
    /// Returns the <code>&lt;div&gt;</code> that should be inserted in the HTML where the reCAPTCHA should go.
    /// </summary>
    /// <remarks>
    /// I'm still not sure if this should be a method or not.
    /// </remarks>
    public string ReCaptchaLocationInclude { get { return _ReCaptchaLocationInclude.Replace("%SITEKEY%", _ReCaptchaSiteKey).Replace("%EXTRACLASSES%", string.Join(" ", ExtraClasses)); } }

    /// <summary>
    /// Creates a new instance of the <see cref="ReCaptchaValidator"/>.
    /// </summary>
    /// <param name="reCaptchaSecret">The reCAPTCHA secret.</param>
    /// <param name="reCaptchaSiteKey">The reCAPTCHA site key.</param>
    public ReCaptchaValidator(string reCaptchaSecret, string reCaptchaSiteKey)
    {
        _ReCaptchaSecret = reCaptchaSecret;
        _ReCaptchaSiteKey = reCaptchaSiteKey;
    }

    /// <summary>
    /// Determines if the reCAPTCHA response in a <code>NameValueCollection</code> passed validation.
    /// </summary>
    /// <param name="form">The <code>Request.Form</code> to validate.</param>
    /// <returns>A boolean value indicating success.</returns>
    public bool Validate(NameValueCollection form)
    {
        string reCaptchaSecret = _ReCaptchaSecret;
        string reCaptchaResponse = form["g-recaptcha-response"];
        bool passedReCaptcha = false;

        using (WebClient client = new WebClient())
        {
            byte[] response = client.UploadValues("https://www.google.com/recaptcha/api/siteverify",
                                                    new NameValueCollection() { { "secret", reCaptchaSecret }, { "response", reCaptchaResponse } });

            string reCaptchaResult = System.Text.Encoding.UTF8.GetString(response);

            if (reCaptchaResult.IndexOf("\"success\": true") > 0)
                passedReCaptcha = true;
        }

        return passedReCaptcha;
    }
}

Usage:

string reCaptchaSecret = "";
string reCaptchaSiteKey = "";

ReCaptchaValidator rcv = new ReCaptchaValidator(reCaptchaSecret, reCaptchaSiteKey);
bool passedReCaptcha = rcv.Validate(Request.Form);

It should be pretty self-explanatory. You can use the ReCaptchaValidator.HeadScriptInclude to get the entire <script> tag for the head, and ReCaptchaValidator.ReCaptchaLocationInclude to get the <div> element for placement in the body. These aren't demonstrated here, but are easy to implement.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Some quick shots at the code

  • by retrieving the ReCaptchaLocationInclude property an ArgumentNullException is thrown, because you didn't initialize the ExtraClasses property. I would also like to suggest changing this property from autoimplemented to a normal one, so you can validate any set value.

  • bool passedReCaptcha = false; is not really a good name here. so I wouldn't use this variable at all. Instead I would replace this

    if (reCaptchaResult.IndexOf("\"success\": true") > 0)
        passedReCaptcha = true;  
    

    with

        return (reCaptchaResult.IndexOf("\"success\": true") > 0);
    

    and for the IDE's love add a return false; at the end of the method. If you don't want to do this, it is fine too, but you should replace the former if with passedReCaptcha = (reCaptchaResult.IndexOf("\"success\": true") > 0);

\$\endgroup\$
1
  • \$\begingroup\$ Yeah I just hit that exception when I was testing that, easy enough to fix just add rcv.ExtraClasses = new List<string>(); before you call it, but I'll definitely make these changes! \$\endgroup\$ Aug 11, 2015 at 15:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.