ReCaptcha for Rails - With AJAX Validation
This plugin implements view helpers to generate ReCaptcha code,
to interface with the HTTP API for captcha verification, a DSL
to generate a
before_filter and the necessary hacky code to
implement AJAX captcha validation.
For AJAX validation, a request to ReCaptcha HTTP server must be made in order to verify the user input, but captchas can be checked only once.
The plugin, thus, in case of a successful verification, saves
uses the Rails
flash to temporarily save this status in the
session and then the before filter skips the verification via
the ReCaptcha HTTP service.
gem install panmind-recaptcha
Or via Rails Plugin:
rails plugin install git://github.com/Panmind/recaptcha.git
In your config/environment.rb:
Panmind::Recaptcha.set( :private_key => 'your private key', :public_key => 'your public key' )
In your controller, say, the
UsersController for a signup action:
require_valid_captcha :only => :create private def invalid_captcha @user = User.new params[:user] @user.errors.add_to_base('Captcha failed') render :new, :layout => 'login' end
invalid_captcha method is called by the plugin when captcha
verification fails, and must be overwritten or a
exception will be thrown.
In your view:
<%= recaptcha :label => 'Are you human?', :theme => 'clean' %>
You can pass any
RecaptchaOptions valid option, as stated by the
service documentation. The only nonstandard option
:label is used
by the plugin to print a label before the captcha widget.
To cache the results of a successful captcha verification, you need
simply to pass the
:ajax => true option to the
require_valid_captcha :only => :create, :ajax => true
When the form is validated via AJAX, the maybe successful result will
be saved in the
flash (thus set in the session store); when the form is
then submitted via a plain HTTP request, verification will be skipped.
On Panmind we use our
jquery.ajax-validation plugin, that you
can download from http://github.com/Panmind/jquery-ajax-nav and
On the backend, an example checker that returns different HTTP status code follows:
def signup_checker # If no email was provided, return 400 if params[:email].blank? render :nothing => true, :status => :bad_request and return end email = CGI.unescape(params[:email]) # more thorough checks on email should go here # If an user with this email already exist, return 406 if User.exists?(['email = ?', email]) render :nothing => true, :status => :not_acceptable and return end unless valid_captcha? invalid_captcha and return end save_solved_captcha # This method sets a flag in the flash render :nothing => true, :status => :ok end
HTTP status when validation fails, thus the
must contain a special
render when the request comes from XHR:
def invalid_captcha # If the captcha is not valid, return a 412 (precondition failed) render :nothing => true, :status => 412 and return true if request.xhr? # Same invalid_captcha code as above end
The latest XHR specification from the w3c states that cookies set by responses to requests sent via XHR are to be honored by the browser.
The code was tested with IE (6,7,8), Safari (4, 5), Firefox 3, Chrome 5 and Opera 10.
As long as you use a session store backed on the server or cryptographically sign the cookies used by the session cookie store (as Rails does by default) there is no way to bypass the captcha when AJAX validation is enabled.
Tested with Rails 3.0.3 running under Ruby 1.9.2p0. running under Ruby 1.9.1-p378.