Thursday, August 13, 2009

Recapthca for Expression Engine Member Registration

Well, due to some recent drama involving blog spam at Grist, I had the opportunity to cook up an ExpressionEngine extension that implements reCaptcha for EE signup.

EE provides a convenient hook for overriding the (rather weak) native EE captcha, so getting the captcha to appear on the signup form is simply an exercise in taking the convenient reCaptcha PHP library and invoking its get_html method at the appropriate time. The resulting HTML simply overrides the result of the usual captcha method from EE. Here are the relevant lines from the extension:


require_once($PREFS->ini('system_folder_path').'extensions
/mp_recaptcharecaptchalib.php');

$EXT->end_script = TRUE;
return recaptcha_get_html($PREFS->ini('recaptcha_public_key'));

Now the captcha appears on ths signup form, and it's time to turn our attention to processing. There are three requirements: 1) we need to invoke a check of the reCaptcha at the appropriate moment 2) we need to cleanly pass an error back to the signup process on failure and 3) we need to override or at least mask the native captcha check. Here's my approach:

EE provides the
member_member_register_start hook at the beginning of the registration routine. At that point it's quite easy to do the reCaptcha check:

require_once($PREFS->ini('system_folder_path').'extensions/mp_recaptcha/recaptchalib.php');
$resp = recaptcha_check_answer ($reCaptchaPrivateKey,
$IN->IP,
$_POST["recaptcha_challenge_field"],
$_POST["recaptcha_response_field"]);

But how to handle the response? member_member_register_start only allows injection of logic into the registration process (ie -- you can't affect the return value of the method in which it appears.) You can, however, affect the session and any globals. So here's the trick I used.

In the registration form, I added:

<input type="hidden" name="captcha" value="1">


And in the method involked when the captcha is created, I did the following:

$DB->query("INSERT INTO exp_captcha (date, ip_address, word) VALUES (UNIX_TIMESTAMP(), '".$IN->IP."', '1')");

This means that EE will always be expecting a captcha response of "1", and will always get it UNLESS some outside force intervenes. This is where the result of the reCaptcha web service check comes in. If the result is successfull, we do nothing, and allow EE to think its native captcha check went perfectly. If the result indicates failure, then I do the following in the method invoked at member_member_register_start:

$_POST['captcha'] = '';

This little change will cause EE's native captcha check to think that it has failed, and produce its normal errors upon a captcha failure.

I'd be happy to provide the entire extension to anyone who is interested, but I feel like it needs a little cleaning, documenting and generalization in order to stand on its own two feet. Perhaps I'll post it here soon. Until then, let me know if you'd like a copy.