new_google_recaptcha 1.0.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +113 -5
- data/lib/new_google_recaptcha/validator.rb +19 -4
- data/lib/new_google_recaptcha/version.rb +1 -1
- data/lib/new_google_recaptcha/view_ext.rb +36 -15
- data/lib/new_google_recaptcha.rb +32 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a187e51b64407ccc6cae4186568b1f0deac59c601e6b095424dcb5aad7243edd
|
4
|
+
data.tar.gz: 5660c73c953982985bd3b926071e38040e64615cf9bfed9342cb71d2eed05ddf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4032d4cfa438d70622a2f7b49f4af670b52f6e67fe81638cbb50396fdf240fa356fc23a3417d8b57ba78d2e2fef294f59bcd62037352fcda7d801066837d712
|
7
|
+
data.tar.gz: bd804e4ace434e6dab65d07eef3988c9d6880671a69ee04d8fe081c964ca6b4b94818506165af14aa9a09ded729f0806e77ff0cd637cf36401f4268f4169971d
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# Google Recaptcha v3 + Rails
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/igorkasyanchuk/new_google_recaptcha)
|
4
|
+
[](https://www.railsjazz.com)
|
5
|
+
[](https://www.patreon.com/igorkasyanchuk)
|
6
|
+
|
7
|
+
Integrate Google Recaptcha v3 with Rails app.
|
4
8
|
|
5
9
|
Google Recaptcha console: https://www.google.com/recaptcha/admin#list
|
6
10
|
|
@@ -44,6 +48,31 @@ Recaptcha v3 documentation: https://developers.google.com/recaptcha/docs/v3
|
|
44
48
|
render :new
|
45
49
|
end
|
46
50
|
end
|
51
|
+
|
52
|
+
|
53
|
+
# or
|
54
|
+
# if you need to capture a humanity `score` from Google
|
55
|
+
# before you need to add a column for example `humanity_score` (type: float) where this score will be saved.
|
56
|
+
|
57
|
+
|
58
|
+
def create
|
59
|
+
@post = Post.new(post_params)
|
60
|
+
humanity_details =
|
61
|
+
NewGoogleRecaptcha.get_humanity_detailed(
|
62
|
+
params[:new_google_recaptcha_token],
|
63
|
+
"checkout",
|
64
|
+
NewGoogleRecaptcha.minimum_score,
|
65
|
+
@post
|
66
|
+
)
|
67
|
+
|
68
|
+
@post.humanity_score = humanity_details[:score]
|
69
|
+
|
70
|
+
if humanity_details[:is_human] && @post.save
|
71
|
+
redirect_to @post, notice: 'Post was successfully created.'
|
72
|
+
else
|
73
|
+
render :new
|
74
|
+
end
|
75
|
+
end
|
47
76
|
```
|
48
77
|
|
49
78
|
There are two mandatory arguments for `human?` method:
|
@@ -67,6 +96,16 @@ like this:
|
|
67
96
|
NewGoogleRecaptcha.human?(params[:new_google_recaptcha_token], "checkout")
|
68
97
|
```
|
69
98
|
|
99
|
+
### Saving humanity score from Google in your model
|
100
|
+
|
101
|
+
`get_humanity_detailed` method acts like `human?` method, the only difference is that it returns following hash with three key-value pairs:
|
102
|
+
|
103
|
+
- `is_human` - whether actor is a human or not (same as result of `human?` method)
|
104
|
+
- `score` - actual humanity score from recaptcha response
|
105
|
+
- `model` - model which you trying to save
|
106
|
+
|
107
|
+
It could be handy if you want to store score in db or put it into logs or smth else. Real example is above in the code samples.
|
108
|
+
|
70
109
|
Add to your navigation links `data-turbolinks="false"` to make it works with `turbolinks`.
|
71
110
|
|
72
111
|
## Installation
|
@@ -90,20 +129,71 @@ And edit new_google_recaptcha.rb and enter your site_key and secret_key.
|
|
90
129
|
|
91
130
|
## API
|
92
131
|
|
93
|
-
|
132
|
+
`NewGoogleRecaptcha.human?(token, model)` or `NewGoogleRecaptcha.get_humanity_detailed(token, model)` in contoller
|
94
133
|
|
95
134
|
- token is received from google, must be sent to backend
|
96
135
|
- model optional parameter. if you want to add error to model.
|
97
136
|
|
98
|
-
|
137
|
+
`<%= include_recaptcha_js %>` in layout (by using yield)
|
99
138
|
|
100
139
|
Include Google Recaptcha v3 JS into your Rails app. In head, right before `</head>`.
|
101
140
|
|
102
|
-
|
141
|
+
`<%= recaptcha_action(action_name) %>` in view
|
103
142
|
|
104
143
|
Action where recaptcha action was executed. Actions could be viewed in Admin console. More docs: https://developers.google.com/recaptcha/docs/v3. Action name could be "comments", "checkout", etc. Put any name and check scores in console.
|
105
144
|
|
145
|
+
## How to add to the Devise
|
146
|
+
|
147
|
+
Generate Devise controllers and views, and edit "create" method.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class Users::RegistrationsController < Devise::RegistrationsController
|
151
|
+
...
|
152
|
+
def create
|
153
|
+
build_resource(sign_up_params)
|
154
|
+
|
155
|
+
NewGoogleRecaptcha.human?(
|
156
|
+
params[:new_google_recaptcha_token],
|
157
|
+
"user",
|
158
|
+
NewGoogleRecaptcha.minimum_score,
|
159
|
+
resource) && resource.save
|
160
|
+
|
161
|
+
yield resource if block_given?
|
162
|
+
if resource.persisted?
|
163
|
+
if resource.active_for_authentication?
|
164
|
+
set_flash_message! :notice, :signed_up
|
165
|
+
sign_up(resource_name, resource)
|
166
|
+
respond_with resource, location: after_sign_up_path_for(resource)
|
167
|
+
else
|
168
|
+
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
|
169
|
+
expire_data_after_sign_in!
|
170
|
+
respond_with resource, location: after_inactive_sign_up_path_for(resource)
|
171
|
+
end
|
172
|
+
else
|
173
|
+
clean_up_passwords resource
|
174
|
+
set_minimum_password_length
|
175
|
+
respond_with resource
|
176
|
+
end
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
## How to use in test or specs
|
181
|
+
|
182
|
+
At the end of the spec/rails_helper.rb put:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
module NewGoogleRecaptcha
|
186
|
+
def self.human?(*attrs)
|
187
|
+
true
|
188
|
+
end
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
Tests are located in `specs` folder and `test/dummy/tests` folder.
|
193
|
+
|
194
|
+
|
106
195
|
## I18n support
|
196
|
+
|
107
197
|
reCAPTCHA passes one types of error explanation to a linked model. It will use the I18n gem
|
108
198
|
to translate the default error message if I18n is available. To customize the messages to your locale,
|
109
199
|
add these keys to your I18n backend:
|
@@ -119,11 +209,21 @@ en:
|
|
119
209
|
verification_human: 'Fail'
|
120
210
|
```
|
121
211
|
|
212
|
+
## Badge position
|
213
|
+
|
214
|
+
NewGoogleRecaptcha allows you to render badge in different positions
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
<%= include_recaptcha_js badge: "bottomleft" %>
|
218
|
+
```
|
219
|
+
|
220
|
+
Three of existing `badge` values are `bottomright`, `bottomleft` and `inline`.
|
221
|
+
'inline' lets you position it with CSS.
|
222
|
+
|
122
223
|
## TODO
|
123
224
|
|
124
225
|
- check everything works with turbolinks
|
125
226
|
- allow custom ID for input
|
126
|
-
- return score ?
|
127
227
|
- more tests
|
128
228
|
- handle exceptions with timeouts, json is not parsed
|
129
229
|
- add support for non-Rails apps
|
@@ -137,7 +237,15 @@ You are welcome to contribute.
|
|
137
237
|
* [gilcierweb](https://github.com/gilcierweb)
|
138
238
|
* [RoRElessar](https://github.com/RoRElessar)
|
139
239
|
* [rubyconvict](https://github.com/rubyconvict)
|
240
|
+
* [adelnabiullin](https://github.com/adelnabiullin)
|
241
|
+
* [jhill](https://github.com/jhill)
|
242
|
+
* [dachinat](https://github.com/dachinat)
|
243
|
+
* [edugonch](https://github.com/edugonch)
|
140
244
|
|
141
245
|
## License
|
142
246
|
|
143
247
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
248
|
+
|
249
|
+
|
250
|
+
[<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
|
251
|
+
/>](https://www.railsjazz.com/)
|
@@ -2,9 +2,24 @@ require 'net/http'
|
|
2
2
|
|
3
3
|
module NewGoogleRecaptcha
|
4
4
|
class Validator
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
attr_reader :score
|
6
|
+
attr_reader :token, :action, :minimum_score
|
7
|
+
|
8
|
+
def initialize(token:, action:, minimum_score:)
|
9
|
+
@token = token
|
10
|
+
@action = action
|
11
|
+
@minimum_score = minimum_score
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
uri = NewGoogleRecaptcha.compose_uri(token)
|
16
|
+
|
17
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
18
|
+
http.use_ssl = true if uri.scheme == 'https'
|
19
|
+
result = JSON.parse(http.request(Net::HTTP::Get.new(uri)).body)
|
20
|
+
|
21
|
+
@score = result['score'].to_f
|
22
|
+
|
8
23
|
conditions = []
|
9
24
|
conditions << !!result['success']
|
10
25
|
conditions << (result['score'].to_f >= minimum_score)
|
@@ -12,4 +27,4 @@ module NewGoogleRecaptcha
|
|
12
27
|
conditions.none?(&:!)
|
13
28
|
end
|
14
29
|
end
|
15
|
-
end
|
30
|
+
end
|
@@ -2,24 +2,45 @@ module NewGoogleRecaptcha
|
|
2
2
|
module ViewExt
|
3
3
|
include ActionView::Helpers::TagHelper
|
4
4
|
|
5
|
-
def include_recaptcha_js
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def include_recaptcha_js(opts = {})
|
6
|
+
badge = opts[:badge] ? "&badge=#{opts[:badge]}" : ""
|
7
|
+
generate_recaptcha_callback +
|
8
|
+
javascript_include_tag(
|
9
|
+
"https://www.google.com/recaptcha/api.js?render=#{NewGoogleRecaptcha.site_key}&onload=newGoogleRecaptchaCallback#{badge}",
|
10
|
+
defer: true
|
11
|
+
)
|
9
12
|
end
|
10
13
|
|
11
14
|
def recaptcha_action(action)
|
12
15
|
id = "new_google_recaptcha_token_#{SecureRandom.hex(10)}"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
hidden_field_tag(
|
17
|
+
'new_google_recaptcha_token',
|
18
|
+
nil,
|
19
|
+
readonly: true,
|
20
|
+
'data-google-recaptcha-action' => action,
|
21
|
+
id: id
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def generate_recaptcha_callback
|
28
|
+
javascript_tag %(
|
29
|
+
function newGoogleRecaptchaCallback () {
|
30
|
+
grecaptcha.ready(function () {
|
31
|
+
var elements = document.querySelectorAll('[data-google-recaptcha-action]')
|
32
|
+
Array.prototype.slice.call(elements).forEach(function (el) {
|
33
|
+
var action = el.dataset.googleRecaptchaAction
|
34
|
+
if (!action) return
|
35
|
+
grecaptcha
|
36
|
+
.execute("#{NewGoogleRecaptcha.site_key}", { action: action })
|
37
|
+
.then(function (token) {
|
38
|
+
el.value = token
|
39
|
+
})
|
40
|
+
})
|
41
|
+
})
|
42
|
+
}
|
43
|
+
)
|
23
44
|
end
|
24
45
|
end
|
25
|
-
end
|
46
|
+
end
|
data/lib/new_google_recaptcha.rb
CHANGED
@@ -10,13 +10,37 @@ module NewGoogleRecaptcha
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.human?(token, action, minimum_score = self.minimum_score, model = nil)
|
13
|
-
is_valid =
|
13
|
+
is_valid =
|
14
|
+
NewGoogleRecaptcha::Validator.new(
|
15
|
+
token: token,
|
16
|
+
action: action,
|
17
|
+
minimum_score: minimum_score
|
18
|
+
).call
|
19
|
+
|
14
20
|
if model && !is_valid
|
15
21
|
model.errors.add(:base, self.i18n("new_google_recaptcha.errors.verification_human", "Looks like you are not a human"))
|
16
22
|
end
|
23
|
+
|
17
24
|
is_valid
|
18
25
|
end
|
19
26
|
|
27
|
+
def self.get_humanity_detailed(token, action, minimum_score = self.minimum_score, model = nil)
|
28
|
+
validator =
|
29
|
+
NewGoogleRecaptcha::Validator.new(
|
30
|
+
token: token,
|
31
|
+
action: action,
|
32
|
+
minimum_score: minimum_score
|
33
|
+
)
|
34
|
+
|
35
|
+
is_valid = validator.call
|
36
|
+
|
37
|
+
if model && !is_valid
|
38
|
+
model.errors.add(:base, self.i18n("new_google_recaptcha.errors.verification_human", "Looks like you are not a human"))
|
39
|
+
end
|
40
|
+
|
41
|
+
{ is_human: is_valid, score: validator.score, model: model }
|
42
|
+
end
|
43
|
+
|
20
44
|
def self.i18n(key, default)
|
21
45
|
if defined?(I18n)
|
22
46
|
I18n.translate(key, default: default)
|
@@ -25,7 +49,13 @@ module NewGoogleRecaptcha
|
|
25
49
|
end
|
26
50
|
end
|
27
51
|
|
52
|
+
def self.compose_uri(token)
|
53
|
+
URI(
|
54
|
+
"https://www.google.com/recaptcha/api/siteverify?"\
|
55
|
+
"secret=#{self.secret_key}&response=#{token}"
|
56
|
+
)
|
57
|
+
end
|
28
58
|
end
|
29
59
|
|
30
60
|
require_relative "new_google_recaptcha/view_ext"
|
31
|
-
require_relative "new_google_recaptcha/validator"
|
61
|
+
require_relative "new_google_recaptcha/validator"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: new_google_recaptcha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-01-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
|
-
description: Google
|
126
|
+
description: Google reCAPTCHA v3 + Rails (integration)
|
127
127
|
email:
|
128
128
|
- igorkasyanchuk@gmail.com
|
129
129
|
executables: []
|
@@ -161,8 +161,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
161
|
- !ruby/object:Gem::Version
|
162
162
|
version: '0'
|
163
163
|
requirements: []
|
164
|
-
rubygems_version: 3.
|
164
|
+
rubygems_version: 3.3.3
|
165
165
|
signing_key:
|
166
166
|
specification_version: 4
|
167
|
-
summary: Google
|
167
|
+
summary: Google reCAPTCHA v3 + Rails
|
168
168
|
test_files: []
|