Skip to content

Commit

Permalink
Saving reason for recaptcha failure and having better exception messages
Browse files Browse the repository at this point in the history
  • Loading branch information
holli committed Dec 8, 2024
1 parent 04ad183 commit cc13872
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 1 deletion.
23 changes: 22 additions & 1 deletion lib/recaptcha/adapters/controller_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def verify_recaptcha(options = {})

begin
verified = if Recaptcha.invalid_response?(recaptcha_response)
@_recaptcha_failure_reason = if recaptcha_response.nil?
"No recaptcha response/param(:action) found."
else
"Recaptcha response/param(:action) was invalid."
end
false
else
unless options[:skip_remote_ip]
Expand All @@ -26,10 +31,21 @@ def verify_recaptcha(options = {})

success, @_recaptcha_reply =
Recaptcha.verify_via_api_call(recaptcha_response, options.merge(with_reply: true))
unless success
@_recaptcha_failure_reason = if @_recaptcha_reply["score"] &&
@_recaptcha_reply["score"].to_f < options[:minimum_score].to_f
"Recaptcha score didn't exceed the minimum: #{@_recaptcha_reply["score"]} < #{options[:minimum_score]}."
elsif @_recaptcha_reply['error-codes']
"Recaptcha api call returned with error-codes: #{@_recaptcha_reply['error-codes']}."
else
"Recaptcha failure after api call. Api reply: #{@_recaptcha_reply}."
end
end
success
end

if verified
@_recaptcha_failure_reason = nil
flash.delete(:recaptcha_error) if recaptcha_flash_supported? && !model
true
else
Expand All @@ -41,6 +57,7 @@ def verify_recaptcha(options = {})
false
end
rescue Timeout::Error
@_recaptcha_failure_reason = "Recaptcha server unreachable."
if Recaptcha.configuration.handle_timeouts_gracefully
recaptcha_error(
model,
Expand All @@ -57,13 +74,17 @@ def verify_recaptcha(options = {})
end

def verify_recaptcha!(options = {})
verify_recaptcha(options) || raise(VerifyError)
verify_recaptcha(options) || raise(VerifyError, @_recaptcha_failure_reason)
end

def recaptcha_reply
@_recaptcha_reply if defined?(@_recaptcha_reply)
end

def recaptcha_failure_reason
@_recaptcha_failure_reason
end

def recaptcha_error(model, attribute, message)
if model
model.errors.add(attribute, message)
Expand Down
33 changes: 33 additions & 0 deletions test/verify_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def initialize
public :verify_recaptcha!
public :recaptcha_reply
public :recaptcha_response_token
public :recaptcha_failure_reason
end

describe 'controller helpers' do
Expand All @@ -36,6 +37,22 @@ def initialize

assert_equal :foo, @controller.verify_recaptcha!
end

it "raise with informative error message when it fails" do
response_hash = {
success: true,
action: 'homepage',
score: 0.4
}

expect_http_post.to_return(body: response_hash.to_json)

error = assert_raises Recaptcha::VerifyError do
@controller.verify_recaptcha!(minimum_score: 0.9)
end

assert_equal "Recaptcha score didn't exceed the minimum: 0.4 < 0.9.", error.message
end
end

describe "#verify_recaptcha" do
Expand All @@ -59,6 +76,7 @@ def initialize

refute @controller.verify_recaptcha
assert_equal "reCAPTCHA verification failed, please try again.", @controller.flash[:recaptcha_error]
assert_equal "Recaptcha failure after api call. Api reply: {\"foo\"=>\"false\", \"bar\"=>\"invalid-site-secret-key\"}.", @controller.recaptcha_failure_reason
end

it "adds an error to the model" do
Expand All @@ -79,6 +97,7 @@ def initialize

assert @controller.verify_recaptcha(secret_key: key)
assert_nil @controller.flash[:recaptcha_error]
assert_nil @controller.recaptcha_failure_reason
end

it "returns true on success without remote_ip" do
Expand Down Expand Up @@ -304,6 +323,7 @@ def initialize
it "fails when score is below minimum_score" do
refute verify_recaptcha(minimum_score: 0.5)
assert_flash_error
assert_equal "Recaptcha score didn't exceed the minimum: 0.4 < 0.5.", @controller.recaptcha_failure_reason
end

it "fails when response doesn't include a score" do
Expand Down Expand Up @@ -387,6 +407,19 @@ def initialize
end
end

describe "recaptcha_failure_reason" do
let(:default_response_hash) { {
success: true,
score: 0.97,
'error-codes': ['some-api-error']
} }
it "contains the error-codes when reply has those" do
expect_http_post.to_return(body: success_body)
refute verify_recaptcha()
assert_equal "Recaptcha api call returned with error-codes: [\"some-api-error\"].", @controller.recaptcha_failure_reason
end
end

describe "#recaptcha_response_token" do
it "returns an empty string when params are empty and no action is provided" do
@controller.params = {}
Expand Down

0 comments on commit cc13872

Please sign in to comment.