diff --git a/app/controllers/sign_in_security_check_failed_controller.rb b/app/controllers/sign_in_security_check_failed_controller.rb
new file mode 100644
index 00000000000..e249d6a0059
--- /dev/null
+++ b/app/controllers/sign_in_security_check_failed_controller.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class SignInSecurityCheckFailedController < ApplicationController
+ def show
+ analytics.sign_in_security_check_failed_visited
+ end
+end
diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb
index d8a1df59251..87923c2af5a 100644
--- a/app/controllers/users/sessions_controller.rb
+++ b/app/controllers/users/sessions_controller.rb
@@ -99,10 +99,9 @@ def valid_captcha_result?
end
def process_failed_captcha
- flash[:error] = t('errors.messages.invalid_recaptcha_token')
sign_out(:user)
warden.lock!
- redirect_to root_url
+ redirect_to sign_in_security_check_failed_url
end
def recaptcha_form_args
diff --git a/app/services/analytics_events.rb b/app/services/analytics_events.rb
index 4fc309a00a2..3aa149ccb8a 100644
--- a/app/services/analytics_events.rb
+++ b/app/services/analytics_events.rb
@@ -5502,6 +5502,11 @@ def sign_in_page_visit(flash:, **extra)
track_event('Sign in page visited', flash:, **extra)
end
+ # User lands on security check failed page
+ def sign_in_security_check_failed_visited
+ track_event(:sign_in_security_check_failed_visited)
+ end
+
# @param [Boolean] success
# @param [Boolean] new_user
# @param [Boolean] has_other_auth_methods
diff --git a/app/services/marketing_site.rb b/app/services/marketing_site.rb
index 4b67f6f62ab..25e299d6576 100644
--- a/app/services/marketing_site.rb
+++ b/app/services/marketing_site.rb
@@ -10,6 +10,7 @@ class UnknownArticleException < StandardError; end
manage-your-account/add-or-change-your-authentication-method
manage-your-account/personal-key
trouble-signing-in/face-or-touch-unlock
+ trouble-signing-in/security-check-failed
verify-your-identity/accepted-identification-documents
verify-your-identity/how-to-add-images-of-your-state-issued-id
verify-your-identity/verify-your-identity-in-person
diff --git a/app/views/sign_in_security_check_failed/show.html.erb b/app/views/sign_in_security_check_failed/show.html.erb
new file mode 100644
index 00000000000..cefc52ee39d
--- /dev/null
+++ b/app/views/sign_in_security_check_failed/show.html.erb
@@ -0,0 +1,33 @@
+<% self.title = t('security_check_failed.title') %>
+
+<%= render StatusPageComponent.new(status: :warning) do |c| %>
+ <% c.with_header { t('security_check_failed.title') } %>
+
+ <%= t('security_check_failed.details') %>
+ <%= new_tab_link_to(
+ t('security_check_failed.learn_more', app_name: APP_NAME),
+ help_center_redirect_path(
+ category: 'trouble-signing-in',
+ article: 'security-check-failed',
+ ),
+ ) %>
+
+
+
+ - <%= t('security_check_failed.info_p1', app_name: APP_NAME) %>
+ - <%= t('security_check_failed.info_p2_html', piv_cac_url: login_piv_cac_url) %>
+ - <%= t('security_check_failed.info_p3') %>
+
+
+ <% c.with_troubleshooting_options do |tc| %>
+ <% tc.with_header { t('components.troubleshooting_options.default_heading') } %>
+ <% tc.with_option(
+ url: contact_redirect_url,
+ new_tab: true,
+ ).with_content(t('security_check_failed.contact', app_name: APP_NAME)) %>
+ <% end %>
+<% end %>
+
+<%= render PageFooterComponent.new do %>
+ <%= link_to t('forms.buttons.back'), root_url %>
+<% end %>
\ No newline at end of file
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 321cbf9b43a..66dc46dc9ec 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1486,6 +1486,13 @@ risc.security_event.errors.typ_error: typ header must be %{expected_typ}
saml_idp.auth.error.title: Error
saml_idp.shared.saml_post_binding.heading: Submit to continue
saml_idp.shared.saml_post_binding.no_js: JavaScript seems to be turned off in your browser. Normally this step happens automatically, but because you have JavaScript turned off, please click the submit button to continue signing in or signing out.
+security_check_failed.contact: Contact %{app_name} support
+security_check_failed.details: We don’t recognize the device or browser you’re signing in from.
+security_check_failed.info_p1: 'Use the same device and browser you created your %{app_name} account with.'
+security_check_failed.info_p2_html: Try signing in with your government employee ID if you are a government employee.
+security_check_failed.info_p3: Turn off any VPN, ad blockers, and clear your cache.
+security_check_failed.learn_more: Learn more about %{app_name}’s security checks
+security_check_failed.title: Security check failed
service_providers.errors.generic_sp_name: This agency
service_providers.errors.inactive.button_text: View my %{app_name} account
service_providers.errors.inactive.heading: '%{sp_name} no longer uses %{app_name}'
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 3b2d7bac396..b20063c0f7f 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -1498,6 +1498,13 @@ risc.security_event.errors.typ_error: encabezado de tipo debe ser %{expected_typ
saml_idp.auth.error.title: Error
saml_idp.shared.saml_post_binding.heading: Enviar para continuar
saml_idp.shared.saml_post_binding.no_js: JavaScript parece estar desactivado en su navegador. Por lo general, este paso es automático, pero, debido a que tiene JavaScript desactivado, haga clic en el botón Enviar para continuar iniciando sesión o cerrándola.
+security_check_failed.contact: Contacte con el servicio de asistencia de %{app_name}
+security_check_failed.details: No reconocemos el dispositivo o el navegador desde el que está iniciando sesión.
+security_check_failed.info_p1: 'Utilice el mismo dispositivo y navegador con el que creó su cuenta en %{app_name}.'
+security_check_failed.info_p2_html: Intente iniciar sesión con su identificación de empleado del gobierno si es un empleado del gobierno.
+security_check_failed.info_p3: Desactive cualquier VPN, bloqueador de anuncios y borre su caché.
+security_check_failed.learn_more: Obtenga más información sobre las comprobaciones de seguridad de %{app_name}
+security_check_failed.title: Falló la comprobación de seguridad
service_providers.errors.generic_sp_name: Esta agencia
service_providers.errors.inactive.button_text: Ver mi cuenta de %{app_name}
service_providers.errors.inactive.heading: '%{sp_name} ya no utiliza %{app_name}'
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index f610bef890b..24d12714ecb 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -1486,6 +1486,13 @@ risc.security_event.errors.typ_error: l’en-tête typ doit être %{expected_typ
saml_idp.auth.error.title: Erreur
saml_idp.shared.saml_post_binding.heading: Valider pour continuer
saml_idp.shared.saml_post_binding.no_js: JavaScript semble être désactivé dans votre navigateur. Normalement, cette étape se produit automatiquement, mais comme vous avez désactivé JavaScript, veuillez cliquer sur le bouton « Valider » pour poursuivre la connexion ou la déconnexion.
+security_check_failed.contact: Contactez l’assistance %{app_name}
+security_check_failed.details: Nous ne reconnaissons pas l’appareil ou le navigateur à partir duquel vous vous connectez.
+security_check_failed.info_p1: 'Utilisez le même appareil et le même navigateur que ceux avec lesquels vous aviez créé votre compte %{app_name}.'
+security_check_failed.info_p2_html: Essayez de vous connecter avec votre carte d’employé fédéral si vous êtes employé fédéral.
+security_check_failed.info_p3: Désactivez tout éventuel VPN ou bloqueur de publicité et videz votre cache.
+security_check_failed.learn_more: En savoir plus sur les tests de sécurité de %{app_name}
+security_check_failed.title: Échec du test de sécurité
service_providers.errors.generic_sp_name: Cet organisme
service_providers.errors.inactive.button_text: Voir mon compte %{app_name}
service_providers.errors.inactive.heading: '%{sp_name} n’utilise plus %{app_name}'
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index 16fe42cc17c..cf3b59e3fb8 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -1497,6 +1497,13 @@ risc.security_event.errors.typ_error: 类型标头必须是 %{expected_typ}
saml_idp.auth.error.title: 错误
saml_idp.shared.saml_post_binding.heading: 提交来继续
saml_idp.shared.saml_post_binding.no_js: 你浏览器中的 JavaScript 似乎已关闭。该步骤通常会自动发生,但因为你把 JavaScript 关闭,请点击提交按钮来继续登入或登出。
+security_check_failed.contact: 请联系 %{app_name}支持
+security_check_failed.details: 我们不认识您登录正在使用的设备或浏览器。
+security_check_failed.info_p1: '使用您设立自己 %{app_name} 账户时的同一设备和浏览器。'
+security_check_failed.info_p2_html: '如果您是政府雇员,尝试用您的政府雇员身份证件登录。'
+security_check_failed.info_p3: 关闭任何 VPN 和广告拦截器并清除缓存。
+security_check_failed.learn_more: 了解 %{app_name} 安全检查的更多信息
+security_check_failed.title: 安全检查未通过
service_providers.errors.generic_sp_name: 该机构
service_providers.errors.inactive.button_text: 查看我的 %{app_name} 账户
service_providers.errors.inactive.heading: '%{sp_name} 不再使用 %{app_name}'
diff --git a/config/routes.rb b/config/routes.rb
index 8ba42da6396..068e664ff7c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -183,6 +183,8 @@
post '/test/device_profiling' => 'test/device_profiling#create'
end
+ get '/sign_in_security_check_failed' => 'sign_in_security_check_failed#show'
+
get '/auth_method_confirmation' => 'mfa_confirmation#show'
post '/auth_method_confirmation/skip' => 'mfa_confirmation#skip'
diff --git a/spec/controllers/sign_in_security_check_failed_controller_spec.rb b/spec/controllers/sign_in_security_check_failed_controller_spec.rb
new file mode 100644
index 00000000000..661a1012a6f
--- /dev/null
+++ b/spec/controllers/sign_in_security_check_failed_controller_spec.rb
@@ -0,0 +1,11 @@
+require 'rails_helper'
+
+RSpec.describe SignInSecurityCheckFailedController do
+ describe '#show' do
+ it 'logs an event' do
+ stub_analytics
+ get :show
+ expect(@analytics).to have_logged_event(:sign_in_security_check_failed_visited)
+ end
+ end
+end
diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb
index ad235ebe656..731dc80b198 100644
--- a/spec/controllers/users/sessions_controller_spec.rb
+++ b/spec/controllers/users/sessions_controller_spec.rb
@@ -253,6 +253,18 @@
)
end
+ it 'redirects unsuccessful authentication for failed reCAPTCHA to failed page' do
+ user = create(:user, :fully_registered)
+
+ allow(FeatureManagement).to receive(:sign_in_recaptcha_enabled?).and_return(true)
+ allow(IdentityConfig.store).to receive(:recaptcha_mock_validator).and_return(true)
+ allow(IdentityConfig.store).to receive(:sign_in_recaptcha_score_threshold).and_return(0.2)
+
+ post :create, params: { user: { email: user.email, password: user.password, score: 0.1 } }
+
+ expect(response).to redirect_to sign_in_security_check_failed_url
+ end
+
it 'tracks count of multiple unsuccessful authentication attempts' do
user = create(
:user,
diff --git a/spec/features/users/sign_in_spec.rb b/spec/features/users/sign_in_spec.rb
index 2fb3daf39cd..9ce21ad3c72 100644
--- a/spec/features/users/sign_in_spec.rb
+++ b/spec/features/users/sign_in_spec.rb
@@ -902,6 +902,23 @@
end
end
+ context 'Recaptcha check fails' do
+ let(:user) { create(:user, :fully_registered) }
+ before do
+ allow(FeatureManagement).to receive(:sign_in_recaptcha_enabled?).and_return(true)
+ allow(IdentityConfig.store).to receive(:recaptcha_mock_validator).and_return(true)
+ allow(IdentityConfig.store).to receive(:sign_in_recaptcha_score_threshold).and_return(0.2)
+ end
+
+ it 'redirects user to security check failed page' do
+ visit new_user_session_path
+ fill_in :user_recaptcha_mock_score, with: '0.1'
+ fill_in_credentials_and_submit(user.email, user.password)
+
+ expect(current_path).to eq sign_in_security_check_failed_path
+ end
+ end
+
context 'check_password_compromised feature toggle is true' do
before do
allow(FeatureManagement).to receive(:check_password_enabled?).and_return(true)