recaptcha 4.14.0 → 5.14.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
+
1
2
  # reCAPTCHA
3
+ [![Gem Version](https://badge.fury.io/rb/recaptcha.svg)](https://badge.fury.io/rb/recaptcha)
2
4
 
3
5
  Author: Jason L Perry (http://ambethia.com)<br/>
4
6
  Copyright: Copyright (c) 2007-2013 Jason L Perry<br/>
@@ -6,42 +8,89 @@ License: [MIT](http://creativecommons.org/licenses/MIT/)<br/>
6
8
  Info: https://github.com/ambethia/recaptcha<br/>
7
9
  Bugs: https://github.com/ambethia/recaptcha/issues<br/>
8
10
 
9
- This plugin adds helpers for the [reCAPTCHA API](https://www.google.com/recaptcha). In your
10
- views you can use the `recaptcha_tags` method to embed the needed javascript,
11
- and you can validate in your controllers with `verify_recaptcha` or `verify_recaptcha!`,
12
- which throws an error on failiure.
11
+ This gem provides helper methods for the [reCAPTCHA API](https://www.google.com/recaptcha). In your
12
+ views you can use the `recaptcha_tags` method to embed the needed javascript, and you can validate
13
+ in your controllers with `verify_recaptcha` or `verify_recaptcha!`, which raises an error on
14
+ failure.
15
+
16
+
17
+ # Table of Contents
18
+ 1. [Obtaining a key](#obtaining-a-key)
19
+ 2. [Rails Installation](#rails-installation)
20
+ 3. [Sinatra / Rack / Ruby Installation](#sinatra--rack--ruby-installation)
21
+ 4. [reCAPTCHA V2 API & Usage](#recaptcha-v2-api-and-usage)
22
+ - [`recaptcha_tags`](#recaptcha_tags)
23
+ - [`verify_recaptcha`](#verify_recaptcha)
24
+ - [`invisible_recaptcha_tags`](#invisible_recaptcha_tags)
25
+ 5. [reCAPTCHA V3 API & Usage](#recaptcha-v3-api-and-usage)
26
+ - [`recaptcha_v3`](#recaptcha_v3)
27
+ - [`verify_recaptcha` (use with v3)](#verify_recaptcha-use-with-v3)
28
+ - [`recaptcha_reply`](#recaptcha_reply)
29
+ 6. [I18n Support](#i18n-support)
30
+ 7. [Testing](#testing)
31
+ 8. [Alternative API Key Setup](#alternative-api-key-setup)
32
+
33
+ ## Obtaining a key
34
+
35
+ Go to the [reCAPTCHA admin console](https://www.google.com/recaptcha/admin) to obtain a reCAPTCHA API key.
36
+
37
+ The reCAPTCHA type(s) that you choose for your key will determine which methods to use below.
38
+
39
+ | reCAPTCHA type | Methods to use | Description |
40
+ |----------------------------------------------|----------------|-------------|
41
+ | v3 | [`recaptcha_v3`](#recaptcha_v3) | Verify requests with a [score](https://developers.google.com/recaptcha/docs/v3#score)
42
+ | v2 Checkbox<br/>("I'm not a robot" Checkbox) | [`recaptcha_tags`](#recaptcha_tags) | Validate requests with the "I'm not a robot" checkbox |
43
+ | v2 Invisible<br/>(Invisible reCAPTCHA badge) | [`invisible_recaptcha_tags`](#invisible_recaptcha_tags) | Validate requests in the background |
44
+
45
+ Note: You can _only_ use methods that match your key's type. You cannot use v2 methods with a v3
46
+ key or use `recaptcha_tags` with a v2 Invisible key, for example. Otherwise you will get an
47
+ error like "Invalid key type" or "This site key is not enabled for the invisible captcha."
48
+
49
+ Note: Enter `localhost` or `127.0.0.1` as the domain if using in development with `localhost:3000`.
13
50
 
14
51
  ## Rails Installation
15
52
 
16
- [obtain a reCAPTCHA API key](https://www.google.com/recaptcha/admin). Note: Use localhost or 127.0.0.1 in domain if using localhost:3000.
53
+ **If you are having issues with Rails 7, Turbo, and Stimulus, make sure to check [this Wiki page](https://github.com/ambethia/recaptcha/wiki/Recaptcha-with-Turbo-and-Stimulus)!**
17
54
 
18
- ```Ruby
55
+ ```ruby
19
56
  gem "recaptcha"
20
57
  ```
21
58
 
22
- Keep keys out of the code base with environment variables.<br/>
23
- Set in production and locally use [dotenv](https://github.com/bkeepers/dotenv), make sure to add it above recaptcha.
59
+ You can keep keys out of the code base with environment variables or with Rails [secrets](https://api.rubyonrails.org/classes/Rails/Application.html#method-i-secrets).<br/>
24
60
 
25
- Otherwise see [Alternative API key setup](#alternative-api-key-setup).
61
+ In development, you can use the [dotenv](https://github.com/bkeepers/dotenv) gem. (Make sure to add it above `gem 'recaptcha'`.)
26
62
 
27
- ```
28
- export RECAPTCHA_SITE_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
63
+ See [Alternative API key setup](#alternative-api-key-setup) for more ways to configure or override
64
+ keys. See also the
65
+ [Configuration](https://www.rubydoc.info/github/ambethia/recaptcha/master/Recaptcha/Configuration)
66
+ documentation.
67
+
68
+ ```shell
69
+ export RECAPTCHA_SITE_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
29
70
  export RECAPTCHA_SECRET_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
30
71
  ```
31
72
 
32
- Add `recaptcha_tags` to the forms you want to protect.
73
+ If you have an Enterprise API key:
33
74
 
34
- ```Erb
75
+ ```shell
76
+ export RECAPTCHA_ENTERPRISE = 'true'
77
+ export RECAPTCHA_ENTERPRISE_API_KEY = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyupA'
78
+ export RECAPTCHA_ENTERPRISE_PROJECT_ID = 'my-project'
79
+ ```
80
+
81
+ Add `recaptcha_tags` to the forms you want to protect:
82
+
83
+ ```erb
35
84
  <%= form_for @foo do |f| %>
36
- # ... other tags
85
+ #
37
86
  <%= recaptcha_tags %>
38
- # ... other tags
87
+ #
39
88
  <% end %>
40
89
  ```
41
90
 
42
- And, add `verify_recaptcha` logic to each form action that you've protected.
91
+ Then, add `verify_recaptcha` logic to each form action that you've protected:
43
92
 
44
- ```Ruby
93
+ ```ruby
45
94
  # app/controllers/users_controller.rb
46
95
  @user = User.new(params[:user].permit(:name))
47
96
  if verify_recaptcha(model: @user) && @user.save
@@ -50,6 +99,7 @@ else
50
99
  render 'new'
51
100
  end
52
101
  ```
102
+ Please note that this setup uses [`reCAPTCHA_v2`](#recaptcha-v2-api-and-usage). For a `recaptcha_v3` use, please refer to [`reCAPTCHA_v3 setup`](#examples).
53
103
 
54
104
  ## Sinatra / Rack / Ruby installation
55
105
 
@@ -57,63 +107,125 @@ See [sinatra demo](/demo/sinatra) for details.
57
107
 
58
108
  - add `gem 'recaptcha'` to `Gemfile`
59
109
  - set env variables
60
- - `include Recaptcha::ClientHelper` where you need `recaptcha_tags`
61
- - `include Recaptcha::Verify` where you need `verify_recaptcha`
110
+ - `include Recaptcha::Adapters::ViewMethods` where you need `recaptcha_tags`
111
+ - `include Recaptcha::Adapters::ControllerMethods` where you need `verify_recaptcha`
62
112
 
63
- ## recaptcha_tags
64
113
 
65
- Some of the options available:
114
+ ## reCAPTCHA v2 API and Usage
115
+
116
+ ### `recaptcha_tags`
117
+
118
+ Use this when your key's reCAPTCHA type is "v2 Checkbox".
119
+
120
+ The following options are available:
121
+
122
+ | Option | Description |
123
+ |---------------------|-------------|
124
+ | `:theme` | Specify the theme to be used per the API. Available options: `dark` and `light`. (default: `light`) |
125
+ | `:ajax` | Render the dynamic AJAX captcha per the API. (default: `false`) |
126
+ | `:site_key` | Override site API key from configuration |
127
+ | `:error` | Override the error code returned from the reCAPTCHA API (default: `nil`) |
128
+ | `:size` | Specify a size (default: `nil`) |
129
+ | `:nonce` | Optional. Sets nonce attribute for script. Can be generated via `SecureRandom.base64(32)`. (default: `nil`) |
130
+ | `:id` | Specify an html id attribute (default: `nil`) |
131
+ | `:callback` | Optional. Name of success callback function, executed when the user submits a successful response |
132
+ | `:expired_callback` | Optional. Name of expiration callback function, executed when the reCAPTCHA response expires and the user needs to re-verify. |
133
+ | `:error_callback` | Optional. Name of error callback function, executed when reCAPTCHA encounters an error (e.g. network connectivity) |
134
+ | `:noscript` | Include `<noscript>` content (default: `true`)|
135
+
136
+ [JavaScript resource (api.js) parameters](https://developers.google.com/recaptcha/docs/invisible#js_param):
66
137
 
67
- | Option | Description |
68
- |-------------------|-------------|
69
- | :noscript | Include <noscript> content (default `true`)|
70
- | :theme | Specify the theme to be used per the API. Available options: `dark` and `light`. (default `light`)|
71
- | :ajax | Render the dynamic AJAX captcha per the API. (default `false`)|
72
- | :site_key | Override site API key |
73
- | :error | Override the error code returned from the reCAPTCHA API (default `nil`)|
74
- | :size | Specify a size (default `nil`)|
75
- | :hl | Optional. Forces the widget to render in a specific language. Auto-detects the user's language if unspecified. (See [language codes](https://developers.google.com/recaptcha/docs/language)) |
76
- | :onload | Optional. The name of your callback function to be executed once all the dependencies have loaded. (See [explicit rendering](https://developers.google.com/recaptcha/docs/display#explicit_render))|
77
- | :render | Optional. Whether to render the widget explicitly. Defaults to `onload`, which will render the widget in the first g-recaptcha tag it finds. (See [explicit rendering](https://developers.google.com/recaptcha/docs/display#explicit_render))|
78
- | :nonce | Optional. Sets nonce attribute for script. Can be generated via `SecureRandom.base64(32)`. (default `nil`)|
79
- | :id | Specify an html id attribute (default `nil`)|
80
- | :script | If you do not need to add a script tag by helper you can set the option to false. It's necessary when you add a script tag manualy (default `true`)|
81
- | :callback | Optional. Name of success callback function, executed when the user submits a successful response |
82
- | :expired_callback | Optional. Name of expiration callback function, executed when the reCAPTCHA response expires and the user needs to re-verify. |
83
- | :error_callback | Optional. Name of error callback function, executed when reCAPTCHA encounters an error (e.g. network connectivity) |
138
+ | Option | Description |
139
+ |---------------------|-------------|
140
+ | `:onload` | Optional. The name of your callback function to be executed once all the dependencies have loaded. (See [explicit rendering](https://developers.google.com/recaptcha/docs/display#explicit_render)) |
141
+ | `:render` | Optional. Whether to render the widget explicitly. Defaults to `onload`, which will render the widget in the first g-recaptcha tag it finds. (See [explicit rendering](https://developers.google.com/recaptcha/docs/display#explicit_render)) |
142
+ | `:hl` | Optional. Forces the widget to render in a specific language. Auto-detects the user's language if unspecified. (See [language codes](https://developers.google.com/recaptcha/docs/language)) |
143
+ | `:script` | Alias for `:external_script`. If you do not need to add a script tag by helper you can set the option to `false`. It's necessary when you add a script tag manualy (default: `true`). |
144
+ | `:external_script` | Set to `false` to avoid including a script tag for the external `api.js` resource. Useful when including multiple `recaptcha_tags` on the same page. |
145
+ | `:script_async` | Set to `false` to load the external `api.js` resource synchronously. (default: `true`) |
146
+ | `:script_defer` | Set to `true` to defer loading of external `api.js` until HTML documen has been parsed. (default: `true`) |
147
+
148
+ Any unrecognized options will be added as attributes on the generated tag.
84
149
 
85
150
  You can also override the html attributes for the sizes of the generated `textarea` and `iframe`
86
- elements, if CSS isn't your thing. Inspect the source of `recaptcha_tags` to see these options.
151
+ elements, if CSS isn't your thing. Inspect the [source of `recaptcha_tags`](https://github.com/ambethia/recaptcha/blob/master/lib/recaptcha/helpers.rb)
152
+ to see these options.
87
153
 
88
- ## verify_recaptcha
154
+ Note that you cannot submit/verify the same response token more than once or you will get a
155
+ `timeout-or-duplicate` error code. If you need reset the captcha and generate a new response token,
156
+ then you need to call `grecaptcha.reset()`.
89
157
 
90
- This method returns `true` or `false` after processing the parameters from the reCAPTCHA widget. Why
91
- isn't this a model validation? Because that violates MVC. You can use it like this, or how ever you
92
- like. Passing in the ActiveRecord object is optional, if you do--and the captcha fails to verify--an
93
- error will be added to the object for you to use.
158
+ ### `verify_recaptcha`
94
159
 
95
- Some of the options available:
160
+ This method returns `true` or `false` after processing the response token from the reCAPTCHA widget.
161
+ This is usually called from your controller, as seen [above](#rails-installation).
162
+
163
+ Passing in the ActiveRecord object via `model: object` is optional. If you pass a `model`—and the
164
+ captcha fails to verify—an error will be added to the object for you to use (available as
165
+ `object.errors`).
96
166
 
97
- | Option | Description |
98
- |--------------|-------------|
99
- | :model | Model to set errors.
100
- | :attribute | Model attribute to receive errors. (default :base)
101
- | :message | Custom error message.
102
- | :secret_key | Override secret API key.
103
- | :timeout | The number of seconds to wait for reCAPTCHA servers before give up. (default `3`)
104
- | :response | Custom response parameter. (default: params['g-recaptcha-response'])
105
- | :hostname | Expected hostname or a callable that validates the hostname, see [domain validation](https://developers.google.com/recaptcha/docs/domain_validation) and [hostname](https://developers.google.com/recaptcha/docs/verify#api-response) docs. (default: `nil`, but can be changed by setting `config.hostname`)
106
- | :env | Current environment. The request to verify will be skipped if the environment is specified in configuration under `skip_verify_env`
167
+ Why isn't this a model validation? Because that violates MVC. You can use it like this, or how ever
168
+ you like.
107
169
 
108
- ## invisible_recaptcha_tags
170
+ Some of the options available:
109
171
 
110
- Make sure to read [Invisible reCAPTCHA](https://developers.google.com/recaptcha/docs/invisible).
172
+ | Option | Description |
173
+ |---------------------------|-------------|
174
+ | `:model` | Model to set errors.
175
+ | `:attribute` | Model attribute to receive errors. (default: `:base`)
176
+ | `:message` | Custom error message.
177
+ | `:secret_key` | Override the secret API key from the configuration.
178
+ | `:enterprise_api_key` | Override the Enterprise API key from the configuration.
179
+ | `:enterprise_project_id ` | Override the Enterprise project ID from the configuration.
180
+ | `:timeout` | The number of seconds to wait for reCAPTCHA servers before give up. (default: `3`)
181
+ | `:response` | Custom response parameter. (default: `params['g-recaptcha-response-data']`)
182
+ | `:hostname` | Expected hostname or a callable that validates the hostname, see [domain validation](https://developers.google.com/recaptcha/docs/domain_validation) and [hostname](https://developers.google.com/recaptcha/docs/verify#api-response) docs. (default: `nil`, but can be changed by setting `config.hostname`)
183
+ | `:env` | Current environment. The request to verify will be skipped if the environment is specified in configuration under `skip_verify_env`
184
+
185
+
186
+ ### `invisible_recaptcha_tags`
187
+
188
+ Use this when your key's reCAPTCHA type is "v2 Invisible".
189
+
190
+ For more information, refer to: [Invisible reCAPTCHA](https://developers.google.com/recaptcha/docs/invisible).
191
+
192
+ This is similar to `recaptcha_tags`, with the following additional options that are only available
193
+ on `invisible_recaptcha_tags`:
194
+
195
+ | Option | Description |
196
+ |---------------------|-------------|
197
+ | `:ui` | The type of UI to render for this "invisible" widget. (default: `:button`)<br/>`:button`: Renders a `<button type="submit">` tag with `options[:text]` as the button text.<br/>`:invisible`: Renders a `<div>` tag.<br/>`:input`: Renders a `<input type="submit">` tag with `options[:text]` as the button text. |
198
+ | `:text` | The text to show for the button. (default: `"Submit"`)
199
+ | `:inline_script` | If you do not need this helper to add an inline script tag, you can set the option to `false` (default: `true`).
200
+
201
+ It also accepts most of the options that `recaptcha_tags` accepts, including the following:
202
+
203
+ | Option | Description |
204
+ |---------------------|-------------|
205
+ | `:site_key` | Override site API key from configuration |
206
+ | `:nonce` | Optional. Sets nonce attribute for script tag. Can be generated via `SecureRandom.base64(32)`. (default: `nil`) |
207
+ | `:id` | Specify an html id attribute (default: `nil`) |
208
+ | `:script` | Same as setting both `:inline_script` and `:external_script`. If you only need one or the other, use `:inline_script` and `:external_script` instead. |
209
+ | `:callback` | Optional. Name of success callback function, executed when the user submits a successful response |
210
+ | `:expired_callback` | Optional. Name of expiration callback function, executed when the reCAPTCHA response expires and the user needs to re-verify. |
211
+ | `:error_callback` | Optional. Name of error callback function, executed when reCAPTCHA encounters an error (e.g. network connectivity) |
212
+
213
+ [JavaScript resource (api.js) parameters](https://developers.google.com/recaptcha/docs/invisible#js_param):
214
+
215
+ | Option | Description |
216
+ |---------------------|-------------|
217
+ | `:onload` | Optional. The name of your callback function to be executed once all the dependencies have loaded. (See [explicit rendering](https://developers.google.com/recaptcha/docs/display#explicit_render)) |
218
+ | `:render` | Optional. Whether to render the widget explicitly. Defaults to `onload`, which will render the widget in the first g-recaptcha tag it finds. (See [explicit rendering](https://developers.google.com/recaptcha/docs/display#explicit_render)) |
219
+ | `:hl` | Optional. Forces the widget to render in a specific language. Auto-detects the user's language if unspecified. (See [language codes](https://developers.google.com/recaptcha/docs/language)) |
220
+ | `:external_script` | Set to `false` to avoid including a script tag for the external `api.js` resource. Useful when including multiple `recaptcha_tags` on the same page. |
221
+ | `:script_async` | Set to `false` to load the external `api.js` resource synchronously. (default: `true`) |
222
+ | `:script_defer` | Set to `false` to defer loading of external `api.js` until HTML documen has been parsed. (default: `true`) |
111
223
 
112
224
  ### With a single form on a page
113
225
 
114
226
  1. The `invisible_recaptcha_tags` generates a submit button for you.
115
227
 
116
- ```Erb
228
+ ```erb
117
229
  <%= form_for @foo do |f| %>
118
230
  # ... other tags
119
231
  <%= invisible_recaptcha_tags text: 'Submit form' %>
@@ -127,14 +239,14 @@ Then, add `verify_recaptcha` to your controller as seen [above](#rails-installat
127
239
  1. You will need a custom callback function, which is called after verification with Google's reCAPTCHA service. This callback function must submit the form. Optionally, `invisible_recaptcha_tags` currently implements a JS function called `invisibleRecaptchaSubmit` that is called when no `callback` is passed. Should you wish to override `invisibleRecaptchaSubmit`, you will need to use `invisible_recaptcha_tags script: false`, see lib/recaptcha/client_helper.rb for details.
128
240
  2. The `invisible_recaptcha_tags` generates a submit button for you.
129
241
 
130
- ```Erb
242
+ ```erb
131
243
  <%= form_for @foo, html: {id: 'invisible-recaptcha-form'} do |f| %>
132
244
  # ... other tags
133
245
  <%= invisible_recaptcha_tags callback: 'submitInvisibleRecaptchaForm', text: 'Submit form' %>
134
246
  <% end %>
135
247
  ```
136
248
 
137
- ```Javascript
249
+ ```javascript
138
250
  // app/assets/javascripts/application.js
139
251
  var submitInvisibleRecaptchaForm = function () {
140
252
  document.getElementById("invisible-recaptcha-form").submit();
@@ -147,7 +259,7 @@ Finally, add `verify_recaptcha` to your controller as seen [above](#rails-instal
147
259
 
148
260
  1. Specify `ui` option
149
261
 
150
- ```Erb
262
+ ```erb
151
263
  <%= form_for @foo, html: {id: 'invisible-recaptcha-form'} do |f| %>
152
264
  # ... other tags
153
265
  <button type="button" id="submit-btn">
@@ -157,7 +269,7 @@ Finally, add `verify_recaptcha` to your controller as seen [above](#rails-instal
157
269
  <% end %>
158
270
  ```
159
271
 
160
- ```Javascript
272
+ ```javascript
161
273
  // app/assets/javascripts/application.js
162
274
  document.getElementById('submit-btn').addEventListener('click', function (e) {
163
275
  // do some validation
@@ -172,28 +284,231 @@ var submitInvisibleRecaptchaForm = function () {
172
284
  };
173
285
  ```
174
286
 
175
- ## I18n support
176
- reCAPTCHA passes two types of error explanation to a linked model. It will use the I18n gem
177
- to translate the default error message if I18n is available. To customize the messages to your locale,
178
- add these keys to your I18n backend:
179
287
 
180
- `recaptcha.errors.verification_failed` error message displayed if the captcha words didn't match
181
- `recaptcha.errors.recaptcha_unreachable` displayed if a timeout error occured while attempting to verify the captcha
288
+ ## reCAPTCHA v3 API and Usage
289
+
290
+ The main differences from v2 are:
291
+ 1. you must specify an [action](https://developers.google.com/recaptcha/docs/v3#actions) in both frontend and backend
292
+ 1. you can choose the minimum score required for you to consider the verification a success
293
+ (consider the user a human and not a robot)
294
+ 1. reCAPTCHA v3 is invisible (except for the reCAPTCHA badge) and will never interrupt your users;
295
+ you have to choose which scores are considered an acceptable risk, and choose what to do (require
296
+ two-factor authentication, show a v3 challenge, etc.) if the score falls below the threshold you
297
+ choose
182
298
 
183
- Also you can translate API response errors to human friendly by adding translations to the locale (`config/locales/en.yml`):
299
+ For more information, refer to the [v3 documentation](https://developers.google.com/recaptcha/docs/v3).
184
300
 
185
- ```Yaml
301
+ ### Examples
302
+
303
+ With v3, you can let all users log in without any intervention at all if their score is above some
304
+ threshold, and only show a v2 checkbox recaptcha challenge (fall back to v2) if it is below the
305
+ threshold:
306
+
307
+ ```erb
308
+
309
+ <% if @show_checkbox_recaptcha %>
310
+ <%= recaptcha_tags %>
311
+ <% else %>
312
+ <%= recaptcha_v3(action: 'login', site_key: ENV['RECAPTCHA_SITE_KEY_V3']) %>
313
+ <% end %>
314
+
315
+ ```
316
+
317
+ ```ruby
318
+ # app/controllers/sessions_controller.rb
319
+ def create
320
+ success = verify_recaptcha(action: 'login', minimum_score: 0.5, secret_key: ENV['RECAPTCHA_SECRET_KEY_V3'])
321
+ checkbox_success = verify_recaptcha unless success
322
+ if success || checkbox_success
323
+ # Perform action
324
+ else
325
+ if !success
326
+ @show_checkbox_recaptcha = true
327
+ end
328
+ render 'new'
329
+ end
330
+ end
331
+ ```
332
+
333
+ (You can also find this [example](demo/rails/app/controllers/v3_captchas_controller.rb) in the demo app.)
334
+
335
+ Another example:
336
+
337
+ ```erb
338
+ <%= form_for @user do |f| %>
339
+
340
+ <%= recaptcha_v3(action: 'registration') %>
341
+
342
+ <% end %>
343
+ ```
344
+
345
+ ```ruby
346
+ # app/controllers/users_controller.rb
347
+ def create
348
+ @user = User.new(params[:user].permit(:name))
349
+ recaptcha_valid = verify_recaptcha(model: @user, action: 'registration')
350
+ if recaptcha_valid
351
+ if @user.save
352
+ redirect_to @user
353
+ else
354
+ render 'new'
355
+ end
356
+ else
357
+ # Score is below threshold, so user may be a bot. Show a challenge, require multi-factor
358
+ # authentication, or do something else.
359
+ render 'new'
360
+ end
361
+ end
362
+ ```
363
+
364
+
365
+ ### `recaptcha_v3`
366
+
367
+ Adds an inline script tag that calls `grecaptcha.execute` for the given `site_key` and `action` and
368
+ calls the `callback` with the resulting response token. You need to verify this token with
369
+ [`verify_recaptcha`](#verify_recaptcha-use-with-v3) in your controller in order to get the
370
+ [score](https://developers.google.com/recaptcha/docs/v3#score).
371
+
372
+ By default, this inserts a hidden `<input type="hidden" class="g-recaptcha-response">` tag. The
373
+ value of this input will automatically be set to the response token (by the default callback
374
+ function). This lets you include `recaptcha_v3` within a `<form>` tag and have it automatically
375
+ submit the token as part of the form submission.
376
+
377
+ Note: reCAPTCHA actually already adds its own hidden tag, like `<textarea
378
+ id="g-recaptcha-response-data-100000" name="g-recaptcha-response-data" class="g-recaptcha-response">`,
379
+ immediately ater the reCAPTCHA badge in the bottom right of the page — but since it is not inside of
380
+ any `<form>` element, and since it already passes the token to the callback, this hidden `textarea`
381
+ isn't helpful to us.
382
+
383
+ If you need to submit the response token to the server in a different way than via a regular form
384
+ submit, such as via [Ajax](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) or [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API),
385
+ then you can either:
386
+ 1. just extract the token out of the hidden `<input>` or `<textarea>` (both of which will have a
387
+ predictable name/id), like `document.getElementById('g-recaptcha-response-data-my-action').value`, or
388
+ 2. write and specify a custom `callback` function. You may also want to pass `element: false` if you
389
+ don't have a use for the hidden input element.
390
+
391
+ Note that you cannot submit/verify the same response token more than once or you
392
+ will get a `timeout-or-duplicate` error code. If you need reset the captcha and
393
+ generate a new response token, then you need to call `grecaptcha.execute(…)` or
394
+ `grecaptcha.enterprise.execute(…)` again. This helper provides a JavaScript
395
+ method (for each action) named `executeRecaptchaFor{action}` to make this
396
+ easier. That is the same method that is invoked immediately. It simply calls
397
+ `grecaptcha.execute` or `grecaptcha.enterprise.execute` again and then calls the
398
+ `callback` function with the response token.
399
+
400
+ You will also get a `timeout-or-duplicate` error if too much time has passed between getting the
401
+ response token and verifying it. This can easily happen with large forms that take the user a couple
402
+ minutes to complete. Unlike v2, where you can use the `expired-callback` to be notified when the
403
+ response expires, v3 appears to provide no such callback. See also
404
+ [1](https://github.com/google/recaptcha/issues/281) and
405
+ [2](https://stackoverflow.com/questions/54437745/recaptcha-v3-how-to-deal-with-expired-token-after-idle).
406
+
407
+ To deal with this, it is recommended to call the "execute" in your form's submit handler (or
408
+ immediately before sending to the server to verify if not using a form) rather than using the
409
+ response token that gets generated when the page first loads. The `executeRecaptchaFor{action}`
410
+ function mentioned above can be used if you want it to invoke a callback, or the
411
+ `executeRecaptchaFor{action}Async` variant if you want a `Promise` that you can `await`. See
412
+ [demo/rails/app/views/v3_captchas/index.html.erb](demo/rails/app/views/v3_captchas/index.html.erb)
413
+ for an example of this.
414
+
415
+ This helper is similar to the [`recaptcha_tags`](#recaptcha_tags)/[`invisible_recaptcha_tags`](#invisible_recaptcha_tags) helpers
416
+ but only accepts the following options:
417
+
418
+ | Option | Description |
419
+ |---------------------|-------------|
420
+ | `:site_key` | Override site API key |
421
+ | `:action` | The name of the [reCAPTCHA action](https://developers.google.com/recaptcha/docs/v3#actions). Actions may only contain alphanumeric characters and slashes, and must not be user-specific. |
422
+ | `:nonce` | Optional. Sets nonce attribute for script. Can be generated via `SecureRandom.base64(32)`. (default: `nil`) |
423
+ | `:callback` | Name of callback function to call with the token. When `element` is `:input`, this defaults to a function named `setInputWithRecaptchaResponseTokenFor#{sanitize_action(action)}` that sets the value of the hidden input to the token. |
424
+ | `:id` | Specify a unique `id` attribute for the `<input>` element if using `element: :input`. (default: `"g-recaptcha-response-data-"` + `action`) |
425
+ | `:name` | Specify a unique `name` attribute for the `<input>` element if using `element: :input`. (default: `g-recaptcha-response-data[action]`) |
426
+ | `:script` | Same as setting both `:inline_script` and `:external_script`. (default: `true`). |
427
+ | `:inline_script` | If `true`, adds an inline script tag that calls `grecaptcha.execute` for the given `site_key` and `action` and calls the `callback` with the resulting response token. Pass `false` if you want to handle calling `grecaptcha.execute` yourself. (default: `true`) |
428
+ | `:element` | The element to render, if any (default: `:input`)<br/>`:input`: Renders a hidden `<input type="hidden">` tag. The value of this will be set to the response token by the default `setInputWithRecaptchaResponseTokenFor{action}` callback.<br/>`false`: Doesn't render any tag. You'll have to add a custom callback that does something with the token. |
429
+ | `:turbolinks` | If `true`, calls the js function which executes reCAPTCHA after all the dependencies have been loaded. This cannot be used with the js param `:onload`. This makes reCAPTCHAv3 usable with turbolinks. |
430
+ | `:ignore_no_element` | If `true`, adds null element checker for forms that can be removed from the page by javascript like modals with forms. (default: true) |
431
+
432
+ [JavaScript resource (api.js) parameters](https://developers.google.com/recaptcha/docs/invisible#js_param):
433
+
434
+ | Option | Description |
435
+ |---------------------|-------------|
436
+ | `:onload` | Optional. The name of your callback function to be executed once all the dependencies have loaded. (See [explicit rendering](https://developers.google.com/recaptcha/docs/display#explicit_render))|
437
+ | `:external_script` | Set to `false` to avoid including a script tag for the external `api.js` resource. Useful when including multiple `recaptcha_tags` on the same page.
438
+ | `:script_async` | Set to `true` to load the external `api.js` resource asynchronously. (default: `false`) |
439
+ | `:script_defer` | Set to `true` to defer loading of external `api.js` until HTML documen has been parsed. (default: `false`) |
440
+
441
+ If using `element: :input`, any unrecognized options will be added as attributes on the generated
442
+ `<input>` element.
443
+
444
+ ### `verify_recaptcha` (use with v3)
445
+
446
+ This works the same as for v2, except that you may pass an `action` and `minimum_score` if you wish
447
+ to validate that the action matches or that the score is above the given threshold, respectively.
448
+
449
+ ```ruby
450
+ result = verify_recaptcha(action: 'action/name')
451
+ ```
452
+
453
+ | Option | Description |
454
+ |------------------|-------------|
455
+ | `:action` | The name of the [reCAPTCHA action](https://developers.google.com/recaptcha/docs/v3#actions) that we are verifying. Set to `false` or `nil` to skip verifying that the action matches.
456
+ | `:minimum_score` | Provide a threshold to meet or exceed. Threshold should be a float between 0 and 1 which will be tested as `score >= minimum_score`. (Default: `nil`) |
457
+
458
+ ### Multiple actions on the same page
459
+
460
+ According to https://developers.google.com/recaptcha/docs/v3#placement,
461
+
462
+ > Note: You can execute reCAPTCHA as many times as you'd like with different actions on the same page.
463
+
464
+ You will need to verify each action individually with a separate call to `verify_recaptcha`.
465
+
466
+ ```ruby
467
+ result_a = verify_recaptcha(action: 'a')
468
+ result_b = verify_recaptcha(action: 'b')
469
+ ```
470
+
471
+ Because the response tokens for multiple actions may be submitted together in the same request, they
472
+ are passed as a hash under `params['g-recaptcha-response-data']` with the action as the key.
473
+
474
+ It is recommended to pass `external_script: false` on all but one of the calls to
475
+ `recaptcha` since you only need to include the script tag once for a given `site_key`.
476
+
477
+ ## `recaptcha_reply`
478
+
479
+ After `verify_recaptcha` has been called, you can call `recaptcha_reply` to get the raw reply from recaptcha. This can allow you to get the exact score returned by recaptcha should you need it.
480
+
481
+ ```ruby
482
+ if verify_recaptcha(action: 'login')
483
+ redirect_to @user
484
+ else
485
+ score = recaptcha_reply['score']
486
+ Rails.logger.warn("User #{@user.id} was denied login because of a recaptcha score of #{score}")
487
+ render 'new'
488
+ end
489
+ ```
490
+
491
+ `recaptcha_reply` will return `nil` if the the reply was not yet fetched.
492
+
493
+ ## I18n support
494
+
495
+ reCAPTCHA supports the I18n gem (it comes with English translations)
496
+ To override or add new languages, add to `config/locales/*.yml`
497
+
498
+ ```yaml
499
+ # config/locales/en.yml
186
500
  en:
187
501
  recaptcha:
188
502
  errors:
189
- verification_failed: 'Fail'
503
+ verification_failed: 'reCAPTCHA was incorrect, please try again.'
504
+ recaptcha_unreachable: 'reCAPTCHA verification server error, please try again.'
190
505
  ```
191
506
 
192
507
  ## Testing
193
508
 
194
509
  By default, reCAPTCHA is skipped in "test" and "cucumber" env. To enable it during test:
195
510
 
196
- ```Ruby
511
+ ```ruby
197
512
  Recaptcha.configuration.skip_verify_env.delete("test")
198
513
  ```
199
514
 
@@ -201,21 +516,27 @@ Recaptcha.configuration.skip_verify_env.delete("test")
201
516
 
202
517
  ### Recaptcha.configure
203
518
 
204
- ```Ruby
519
+ ```ruby
205
520
  # config/initializers/recaptcha.rb
206
521
  Recaptcha.configure do |config|
207
522
  config.site_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
208
523
  config.secret_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
524
+
209
525
  # Uncomment the following line if you are using a proxy server:
210
526
  # config.proxy = 'http://myproxy.com.au:8080'
527
+
528
+ # Uncomment the following lines if you are using the Enterprise API:
529
+ # config.enterprise = true
530
+ # config.enterprise_api_key = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyupA'
531
+ # config.enterprise_project_id = 'my-project'
211
532
  end
212
533
  ```
213
534
 
214
535
  ### Recaptcha.with_configuration
215
536
 
216
- For temporary overwrites (not thread safe).
537
+ For temporary overwrites (not thread-safe).
217
538
 
218
- ```Ruby
539
+ ```ruby
219
540
  Recaptcha.with_configuration(site_key: '12345') do
220
541
  # Do stuff with the overwritten site_key.
221
542
  end
@@ -225,7 +546,7 @@ end
225
546
 
226
547
  Pass in keys as options at runtime, for code base with multiple reCAPTCHA setups:
227
548
 
228
- ```Ruby
549
+ ```ruby
229
550
  recaptcha_tags site_key: '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
230
551
 
231
552
  # and
@@ -233,6 +554,38 @@ recaptcha_tags site_key: '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
233
554
  verify_recaptcha secret_key: '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
234
555
  ```
235
556
 
557
+
558
+ ## hCaptcha support
559
+
560
+ [hCaptcha](https://hcaptcha.com) is an alternative service providing reCAPTCHA API.
561
+
562
+ To use hCaptcha:
563
+ 1. Set a site and a secret key as usual
564
+ 2. Set two options in `verify_url` and `api_service_url` pointing to hCaptcha API endpoints.
565
+ 3. Disable a response limit check by setting a `response_limit` to the large enough value (reCAPTCHA is limited by 4000 characters).
566
+ 4. It is not required to change a parameter name as [official docs suggest](https://docs.hcaptcha.com/switch) because API handles standard `g-recaptcha` for compatibility.
567
+
568
+ ```ruby
569
+ # config/initializers/recaptcha.rb
570
+ Recaptcha.configure do |config|
571
+ config.site_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
572
+ config.secret_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
573
+ config.verify_url = 'https://hcaptcha.com/siteverify'
574
+ config.api_server_url = 'https://hcaptcha.com/1/api.js'
575
+ config.response_limit = 100000
576
+ end
577
+ ```
578
+
579
+ hCaptcha uses a scoring system (higher number more likely to be a bot) which is inverse of the reCaptcha scoring system (lower number more likely to be a bot). As such, a `maximum_score` attribute is provided for use with hCaptcha.
580
+
581
+ ```ruby
582
+ result = verify_recaptcha(maximum_score: 0.7)
583
+ ```
584
+
585
+ | Option | Description |
586
+ |------------------|-------------|
587
+ | `:maximum_score` | Provide a threshold to meet or fall below. Threshold should be a float between 0 and 1 which will be tested as `score <= maximum_score`. (Default: `nil`) |
588
+
236
589
  ## Misc
237
590
  - Check out the [wiki](https://github.com/ambethia/recaptcha/wiki) and leave whatever you found valuable there.
238
591
  - [Add multiple widgets to the same page](https://github.com/ambethia/recaptcha/wiki/Add-multiple-widgets-to-the-same-page)