Skip to content

Commit

Permalink
Use Google reCAPTCHA v3.
Browse files Browse the repository at this point in the history
  • Loading branch information
yukimochi committed Dec 21, 2018
1 parent 6862cee commit e71a698
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .env.production.sample
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,8 @@ STREAMING_CLUSTER_NUM=1
# http_proxy=http://gateway.local:8118
# Access control for hidden service.
# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true

# Use Google reCAPTCHA v3
# RECAPTCHA_ENABLED=true
# RECAPTCHA_SITE_KEY=
# RECAPTCHA_SECRET_KEY=
31 changes: 31 additions & 0 deletions app/controllers/auth/registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
before_action :set_sessions, only: [:edit, :update]
before_action :set_instance_presenter, only: [:new, :create, :update]
before_action :set_body_classes, only: [:new, :create, :edit, :update]
prepend_before_action :check_recaptcha, only: [:create]

def destroy
not_found
Expand Down Expand Up @@ -95,4 +96,34 @@ def determine_layout
def set_sessions
@sessions = current_user.session_activations
end

def check_recaptcha
unless is_human?
self.resource = resource_class.new sign_up_params
set_instance_presenter
resource.validate
respond_with_navigational(resource) { render :new }
end
end

concerning :RecaptchaFeature do
if ENV['RECAPTCHA_ENABLED'] == 'true'
def is_human?
g_recaptcha_response = params["g-recaptcha-response"]
return false unless g_recaptcha_response.present?
verify_by_recaptcha g_recaptcha_response
end
def verify_by_recaptcha(g_recaptcha_response)
conn = Faraday.new(url: 'https://www.google.com')
res = conn.post '/recaptcha/api/siteverify', {
secret: ENV['RECAPTCHA_SECRET_KEY'],
response: g_recaptcha_response
}
j = JSON.parse(res.body)
j['success'] && j['score'] > 0.5
end
else
def is_human?; true end
end
end
end
31 changes: 31 additions & 0 deletions app/controllers/auth/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Auth::SessionsController < Devise::SessionsController
skip_before_action :require_no_authentication, only: [:create]
skip_before_action :check_user_permissions, only: [:destroy]
prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
prepend_before_action :check_recaptcha, only: [:create]
before_action :set_instance_presenter, only: [:new]
before_action :set_body_classes

Expand Down Expand Up @@ -127,4 +128,34 @@ def home_paths(resource)
def continue_after?
truthy_param?(:continue)
end

def check_recaptcha
unless is_human?
self.resource = resource_class.new sign_in_params
set_instance_presenter
flash.now[:alert] = 'BOT access detected by reCAPTCHA. Please retry.'
respond_with_navigational(resource) { render :new }
end
end

concerning :RecaptchaFeature do
if ENV['RECAPTCHA_ENABLED'] == 'true'
def is_human?
g_recaptcha_response = params["g-recaptcha-response"]
return false unless g_recaptcha_response.present?
verify_by_recaptcha g_recaptcha_response
end
def verify_by_recaptcha(g_recaptcha_response)
conn = Faraday.new(url: 'https://www.google.com')
res = conn.post '/recaptcha/api/siteverify', {
secret: ENV['RECAPTCHA_SECRET_KEY'],
response: g_recaptcha_response
}
j = JSON.parse(res.body)
j['success'] && j['score'] > 0.5
end
else
def is_human?; true end
end
end
end
3 changes: 2 additions & 1 deletion app/views/about/_registration.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
= f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false

.actions
= f.button :button, t('auth.register'), type: :submit, class: 'button button-primary'
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('auth.register'), type: :submit, class: 'button button-primary', disabled: true

%p.hint.subtle-hint=t('auth.agreement_html', rules_path: about_more_path, terms_path: terms_path)
3 changes: 2 additions & 1 deletion app/views/auth/confirmations/finish_signup.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
= f.input :email, wrapper: :with_label, required: true, hint: false

.actions
= f.submit t('auth.confirm_email'), class: 'button'
= render partial: 'auth/shared/recaptcha'
= f.submit t('auth.confirm_email'), class: 'button', disabled: true
3 changes: 2 additions & 1 deletion app/views/auth/confirmations/new.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
= f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false

.actions
= f.button :button, t('auth.resend_confirmation'), type: :submit
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('auth.resend_confirmation'), type: :submit, disabled: true

.form-footer= render 'auth/shared/links'
3 changes: 2 additions & 1 deletion app/views/auth/passwords/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, required: true

.actions
= f.button :button, t('auth.set_new_password'), type: :submit
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('auth.set_new_password'), type: :submit, disabled: true
- else
%p.hint= t('users.seamless_external_login')

Expand Down
3 changes: 2 additions & 1 deletion app/views/auth/passwords/new.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
= f.input :email, autofocus: true, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }, hint: false

.actions
= f.button :button, t('auth.reset_password'), type: :submit
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('auth.reset_password'), type: :submit, disabled: true

.form-footer= render 'auth/shared/links'
3 changes: 2 additions & 1 deletion app/views/auth/registrations/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@


.actions
= f.button :button, t('generic.save_changes'), type: :submit
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('generic.save_changes'), type: :submit, disabled: true
- else
%p.hint= t('users.seamless_external_login')

Expand Down
3 changes: 2 additions & 1 deletion app/views/auth/registrations/new.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
%p.hint= t('auth.agreement_html', rules_path: about_more_path, terms_path: terms_path)

.actions
= f.button :button, t('auth.register'), type: :submit
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('auth.register'), type: :submit, disabled: true

.form-footer= render 'auth/shared/links'
3 changes: 2 additions & 1 deletion app/views/auth/sessions/new.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false

.actions
= f.button :button, t('auth.login'), type: :submit
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('auth.login'), type: :submit, disabled: true

- if devise_mapping.omniauthable? and resource_class.omniauth_providers.any?
.simple_form.alternative-login
Expand Down
3 changes: 2 additions & 1 deletion app/views/auth/sessions/two_factor.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
= f.input :otp_attempt, type: :number, wrapper: :with_label, label: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, autofocus: true

.actions
= f.button :button, t('auth.login'), type: :submit
= render partial: 'auth/shared/recaptcha'
= f.button :button, t('auth.login'), type: :submit, disabled: true

- if Setting.site_contact_email.present?
%p.hint.subtle-hint= t('users.otp_lost_help_html', email: mail_to(Setting.site_contact_email, nil))
15 changes: 15 additions & 0 deletions app/views/auth/shared/_recaptcha.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
- if ENV['RECAPTCHA_ENABLED'] == 'true'
%script(src='https://www.google.com/recaptcha/api.js?render=#{ENV['RECAPTCHA_SITE_KEY']}')
:javascript
grecaptcha.ready(() => {
grecaptcha.execute('#{ENV['RECAPTCHA_SITE_KEY']}', { action: 'login' })
.then(t => {
r = document.createElement('textarea');
r.setAttribute('name', 'g-recaptcha-response');
r.setAttribute('style', 'display: none');
r.innerText = t;
e = document.getElementById('new_user') || document.getElementById('edit_user')
e.appendChild(r);
document.getElementsByName('button')[0].removeAttribute('disabled');
});
});

0 comments on commit e71a698

Please sign in to comment.