12

I've seen a few guides for how to use the new recaptcha with PHP, but none with Rails. This is the code I have so far:

<script src='https://www.google.com/recaptcha/api.js'></script>

<%= form_for @user, :url => users_path, :html => { :multipart => true } do |f| %>
  <%= f.text_field :name %>
  <div class="g-recaptcha" data-sitekey="..."></div>
  <%= f.submit "Submit" %>
<% end %>

users_controller.rb

class UsersController < ApplicationController

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to users_success_path
    else
      flash[:notice] = "Failed"
      redirect_to new_user_path
    end
  end

How can I verify the response as true or false? Google's documentation on this subject is extremely confusing.

4 Answers4

3

I would look at the recaptcha gem. It looks like they are close to supporting the new API.

rdubya
  • 2,916
  • 1
  • 16
  • 20
  • 2
    I'd prefer not to use the gem. –  Jan 10 '15 at 15:27
  • 2
    @KenAnderson Even if you don't use it, you can look at the source code and see how they implement it. – rdubya Jan 10 '15 at 15:43
  • The only thing I could glean from the source code was to add a `if verify_recaptcha` line, but apparently "verify_recaptcha" is an undefined method. Am I on the right track at least? –  Jan 10 '15 at 15:59
  • 1
    I haven't actually dealt with it, so I can't give too much background unfortunately. It looks like this is the crux of what was changed to support v2 though so maybe you can get some info from it: https://github.com/pangkalizer/recaptcha/commit/4d30e9fda34df11c275a38df0f5e3444d47c4871 The verify.rb file will probably be the most useful for you. – rdubya Jan 10 '15 at 16:12
2

You should be able to do it with HTTParty.

# Gemfile
gem 'httparty'

bundle

# app/models/recaptcha_verifier.rb
class RecaptchaVerifier
  def initialize(response, ip)
    @response = response
    @ip = ip
  end

  def self.verify(response, ip = nil)
    new(response, ip).verify
  end

  def verify
    recaptcha_response = HTTParty.get(recaptcha_url(@response, secret, @ip))
    response_success?(recaptcha_response)
  end

  private
  def recaptcha_url(response, secret, ip)
    "https://www.google.com/recaptcha/api/siteverify?secret=#{secret}&response=#{response}&remoteip=#{ip}"
  end

  def secret
    # load your secret here or hardcode it
  end

  def response_success?(response)
    response.fetch('success')
  end
end

You can use it in your controller or model like so:

class SomeController
  def some_action
    if RecaptchaVerifier.verify(params[:user][:g-recaptcha-response])
      # proceed
    else
      # output some flash warning and render same action or redirect_to :back
    end
  end
end
Milan Köpke
  • 1,133
  • 7
  • 8
  • Should I `rails generate` the recaptcha_verifier model? –  Jan 15 '15 at 15:34
  • Just copy and paste it to app/models/recaptcha_verifier.rb – Milan Köpke Jan 15 '15 at 16:48
  • I don't know if it's because I don't have multi_xml, but I'm getting a undefined_method error for `if RecaptchaVerifier.verify(params[:user][:g-recaptcha-response])`. –  Jan 16 '15 at 18:59
  • try require 'httparty' at top of recaptcha_verifier.rb – Milan Köpke Jan 19 '15 at 11:45
  • I typed `require 'httparty'` at the top but it still doesn't work. But I'm also supposed to require it in application.js aren't I? When I do that, it tells me it can't find the file. Could the file be called something other than `httparty.js`? –  Jan 20 '15 at 20:49
  • You are not supposed to require it at application.js. If you want JS Validation then there is no difference between PHP and Ruby. – Milan Köpke Jan 21 '15 at 12:31
2

This is a very simple basic script. I don't know whether you are expecting something like this. You could get the concept from this and build on it to adapt this to your use case.

require 'uri'
require 'net/http'

uri = URI("http://www.google.com/recaptcha/api/verify")
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true

verify_request = Net::HTTP::Post.new(uri.path)

verify_request["secret"]        = your_private_key
verify_request["remoteip"]  = request.remote_ip, #ip address of the user
verify_request["challenge"] = params[:recaptcha_challenge_field], #recaptcha challenge field value
verify_request["response"]  = params[:recaptcha_response_field] # recaptcha response field value

response = https.request(request)
puts response

#the response will be json and you could parse it check whether the captcha is correct or not.
Sony Mathew
  • 2,929
  • 2
  • 22
  • 29
  • I'm guessing this would go in my model, right? So what would I put in my controller? –  Jan 21 '15 at 15:06
  • You can do this captcha verify in your controller using a before_filter, before you actually process anything and then redirect if the captcha verify fails. No need to take this code to a model. – Sony Mathew Jan 22 '15 at 07:55
  • I posted my controller. What part of your code would go in my `def create`? And specifically where? –  Jan 22 '15 at 14:48
  • There are two things. If you intend to use this recaptcha verify in more than one Controller, then better write the above code as a module in the library and include that module in this controller. And then in a before filter for create method you can check the recaptcha by calling the function defined in the library and then redirect and return if the verify fails. And all of this code would go in the library function. – Sony Mathew Jan 26 '15 at 04:52
  • Could you be a little more clear? How can I call the function? And what do you mean by "module in the library"? –  Jan 27 '15 at 03:10
  • I haven't tested this code. But try this. Create a file as 'recaptcha_verify.rb' in the lib folder in your rails app. It's content should look like http://fpaste.org/175709/ . Then modify your controller like this http://fpaste.org/175723/ . There might be surely some problems because I haven't tested this. But this is structurally how it ought to be. – Sony Mathew Jan 27 '15 at 06:06
  • Wait, do I have to install a Gem for that? –  Jan 28 '15 at 00:31
  • No. You don't have to. – Sony Mathew Jan 28 '15 at 09:09
  • Because I'm getting a routing error "unitialized constant UsersController::RecaptchaVerify". What's that about? –  Jan 28 '15 at 16:53
  • Read through following : 1. http://stackoverflow.com/questions/619840/rails-i-cant-call-a-function-in-a-module-in-lib-what-am-i-doing-wrong?lq=1 2. http://stackoverflow.com/questions/19098663/auto-loading-lib-files-in-rails-4 3. http://stackoverflow.com/questions/1073076/rails-lib-modules-and – Sony Mathew Jan 29 '15 at 10:02
0

I'll add my code here since the accepted answer is somewhat outdated:

def verify_recaptcha(request, params) #returns true if verification succeeded
    require 'net/http'
    uri = URI("https://www.google.com/recaptcha/api/siteverify")
    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true
    verify_request = Net::HTTP::Post.new(uri.path)
    verify_request.set_form_data( 'secret'=> __your_secret_key_goes_here__,
                                  'response' => params['g-recaptcha-response'] )

    googleanswer = https.request(verify_request)
    resultingjson = JSON.parse( googleanswer.body )
    return resultingjson['success']
end

This is using Ruby 2.3.1 and Rails 5.0.2.

MDickten
  • 105
  • 1
  • 10