diff --git a/app/components/block_link_component.rb b/app/components/block_link_component.rb
index fad065041b1..9878ee58641 100644
--- a/app/components/block_link_component.rb
+++ b/app/components/block_link_component.rb
@@ -1,6 +1,8 @@
class BlockLinkComponent < BaseComponent
attr_reader :url, :action, :new_tab, :tag_options
+ alias_method :new_tab?, :new_tab
+
def initialize(url:, action: tag.method(:a), new_tab: false, **tag_options)
@action = action
@url = url
@@ -10,12 +12,12 @@ def initialize(url:, action: tag.method(:a), new_tab: false, **tag_options)
def css_class
classes = ['usa-link', 'block-link', *tag_options[:class]]
- classes << 'usa-link--external' if new_tab
+ classes << 'usa-link--external' if new_tab?
classes
end
def target
- '_blank' if new_tab
+ '_blank' if new_tab?
end
def wrapper(&block)
diff --git a/app/components/troubleshooting_options_component.rb b/app/components/troubleshooting_options_component.rb
index efa7d693b1d..e15e6c25ea8 100644
--- a/app/components/troubleshooting_options_component.rb
+++ b/app/components/troubleshooting_options_component.rb
@@ -4,10 +4,19 @@ class TroubleshootingOptionsComponent < BaseComponent
attr_reader :tag_options
- def initialize(**tag_options)
+ def initialize(options: [], **tag_options)
+ @options_from_constructor = options
@tag_options = tag_options.dup
end
+ def options
+ @options_from_constructor.map(&method(:render)) + get_slot(:options)
+ end
+
+ def options?
+ options.present?
+ end
+
def render?
options?
end
diff --git a/app/controllers/two_factor_authentication/piv_cac_verification_controller.rb b/app/controllers/two_factor_authentication/piv_cac_verification_controller.rb
index 4080fdb2e11..c17654e382f 100644
--- a/app/controllers/two_factor_authentication/piv_cac_verification_controller.rb
+++ b/app/controllers/two_factor_authentication/piv_cac_verification_controller.rb
@@ -71,10 +71,7 @@ def render_show_after_invalid
end
def piv_cac_view_data
- {
- two_factor_authentication_method: 'piv_cac',
- hide_fallback_question: service_provider_mfa_policy.piv_cac_required?,
- }.merge(generic_data)
+ { two_factor_authentication_method: 'piv_cac' }.merge(generic_data)
end
def piv_cac_verification_form
diff --git a/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb b/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb
index c85fc9e4071..5ff7bc822d3 100644
--- a/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb
+++ b/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb
@@ -4,10 +4,6 @@ def header
t('two_factor_authentication.totp_header_text')
end
- def fallback_question
- t('two_factor_authentication.totp_fallback.question')
- end
-
def cancel_link
if reauthn
account_path
@@ -15,5 +11,9 @@ def cancel_link
sign_out_path
end
end
+
+ def redirect_location_step
+ :totp_verification
+ end
end
end
diff --git a/app/presenters/two_factor_auth_code/backup_code_presenter.rb b/app/presenters/two_factor_auth_code/backup_code_presenter.rb
index 9ff4e35abb7..4dd6a264147 100644
--- a/app/presenters/two_factor_auth_code/backup_code_presenter.rb
+++ b/app/presenters/two_factor_auth_code/backup_code_presenter.rb
@@ -10,8 +10,8 @@ def cancel_link
end
end
- def fallback_question
- t('two_factor_authentication.backup_code_fallback.question')
+ def redirect_location_step
+ :backup_code_verification
end
end
end
diff --git a/app/presenters/two_factor_auth_code/generic_delivery_presenter.rb b/app/presenters/two_factor_auth_code/generic_delivery_presenter.rb
index 28d6d6fc4a6..5e353266e90 100644
--- a/app/presenters/two_factor_auth_code/generic_delivery_presenter.rb
+++ b/app/presenters/two_factor_auth_code/generic_delivery_presenter.rb
@@ -27,8 +27,30 @@ def link_path
login_two_factor_options_path
end
- def fallback_links
- raise NotImplementedError
+ def redirect_location_step; end
+
+ def troubleshooting_options
+ [
+ choose_another_method_troubleshooting_option,
+ learn_more_about_authentication_options_troubleshooting_option,
+ ].select(&:present?)
+ end
+
+ def choose_another_method_troubleshooting_option
+ return if link_path.blank?
+ BlockLinkComponent.new(url: link_path).with_content(link_text)
+ end
+
+ def learn_more_about_authentication_options_troubleshooting_option
+ BlockLinkComponent.new(
+ url: help_center_redirect_path(
+ category: 'get-started',
+ article: 'authentication-options',
+ flow: :two_factor_authentication,
+ step: redirect_location_step,
+ ),
+ new_tab: true,
+ ).with_content(t('two_factor_authentication.learn_more'))
end
def remember_device_box_checked?
diff --git a/app/presenters/two_factor_auth_code/personal_key_presenter.rb b/app/presenters/two_factor_auth_code/personal_key_presenter.rb
index 5a1aeee1e06..b9820962987 100644
--- a/app/presenters/two_factor_auth_code/personal_key_presenter.rb
+++ b/app/presenters/two_factor_auth_code/personal_key_presenter.rb
@@ -2,8 +2,8 @@ module TwoFactorAuthCode
class PersonalKeyPresenter < TwoFactorAuthCode::GenericDeliveryPresenter
def initialize; end
- def fallback_question
- t('two_factor_authentication.personal_key_fallback.question')
+ def redirect_location_step
+ :personal_key_verification
end
end
end
diff --git a/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb b/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
index 21e4663fd80..1fc5f0ba08d 100644
--- a/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
+++ b/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
@@ -37,18 +37,10 @@ def phone_call_text
t('two_factor_authentication.otp_delivery_preference.phone_call')
end
- def fallback_question
- t('two_factor_authentication.phone_fallback.question')
- end
-
- def troubleshooting_header
- t('components.troubleshooting_options.default_heading')
- end
-
def troubleshooting_options
[
troubleshoot_change_phone_or_method_option,
- {
+ BlockLinkComponent.new(
url: help_center_redirect_path(
category: 'get-started',
article: 'authentication-options',
@@ -56,19 +48,11 @@ def troubleshooting_options
flow: :two_factor_authentication,
step: :otp_confirmation,
),
- text: t('two_factor_authentication.phone_verification.troubleshooting.code_not_received'),
- new_tab: true,
- },
- {
- url: help_center_redirect_path(
- category: 'get-started',
- article: 'authentication-options',
- flow: :two_factor_authentication,
- step: :otp_confirmation,
- ),
- text: t('two_factor_authentication.learn_more'),
new_tab: true,
- },
+ ).with_content(
+ t('two_factor_authentication.phone_verification.troubleshooting.code_not_received'),
+ ),
+ learn_more_about_authentication_options_troubleshooting_option,
]
end
@@ -81,19 +65,21 @@ def cancel_link
end
end
+ def redirect_location_step
+ :otp_confirmation
+ end
+
private
def troubleshoot_change_phone_or_method_option
if unconfirmed_phone
- {
- url: add_phone_path,
- text: t('two_factor_authentication.phone_verification.troubleshooting.change_number'),
- }
+ BlockLinkComponent.new(url: add_phone_path).with_content(
+ t('two_factor_authentication.phone_verification.troubleshooting.change_number'),
+ )
else
- {
- url: login_two_factor_options_path,
- text: t('two_factor_authentication.login_options_link_text'),
- }
+ BlockLinkComponent.new(url: login_two_factor_options_path).with_content(
+ t('two_factor_authentication.login_options_link_text'),
+ )
end
end
diff --git a/app/presenters/two_factor_auth_code/piv_cac_authentication_presenter.rb b/app/presenters/two_factor_auth_code/piv_cac_authentication_presenter.rb
index ab67d0327f3..d1d13877e87 100644
--- a/app/presenters/two_factor_auth_code/piv_cac_authentication_presenter.rb
+++ b/app/presenters/two_factor_auth_code/piv_cac_authentication_presenter.rb
@@ -46,6 +46,15 @@ def link_path
end
end
+ def troubleshooting_options
+ options = []
+ if service_provider_mfa_policy.allow_user_to_switch_method?
+ options << choose_another_method_troubleshooting_option
+ end
+ options << learn_more_about_authentication_options_troubleshooting_option
+ options
+ end
+
def cancel_link
if reauthn
account_path
@@ -54,17 +63,12 @@ def cancel_link
end
end
- def piv_cac_service_link
- login_two_factor_piv_cac_present_piv_cac_url
+ def redirect_location_step
+ :piv_cac_verification
end
- def fallback_question
- return if @hide_fallback_question
- if service_provider_mfa_policy.allow_user_to_switch_method?
- t('two_factor_authentication.piv_cac_fallback.question')
- else
- ''
- end
+ def piv_cac_service_link
+ login_two_factor_piv_cac_present_piv_cac_url
end
end
end
diff --git a/app/presenters/two_factor_auth_code/webauthn_authentication_presenter.rb b/app/presenters/two_factor_auth_code/webauthn_authentication_presenter.rb
index 7b143474850..8778ad578a9 100644
--- a/app/presenters/two_factor_auth_code/webauthn_authentication_presenter.rb
+++ b/app/presenters/two_factor_auth_code/webauthn_authentication_presenter.rb
@@ -69,6 +69,23 @@ def link_path
end
end
+ def troubleshooting_options
+ options = [choose_another_method_troubleshooting_option].select(&:present?)
+ if platform_authenticator?
+ options << BlockLinkComponent.new(
+ url: help_center_redirect_path(
+ category: 'trouble-signing-in',
+ article: 'face-or-touch-unlock',
+ flow: :two_factor_authentication,
+ step: redirect_location_step,
+ ),
+ new_tab: true,
+ ).with_content(t('instructions.mfa.webauthn_platform.learn_more_help'))
+ end
+ options << learn_more_about_authentication_options_troubleshooting_option
+ options
+ end
+
def cancel_link
if reauthn
account_path
@@ -77,6 +94,10 @@ def cancel_link
end
end
+ def redirect_location_step
+ :webauthn_verification
+ end
+
def multiple_factors_enabled?
service_provider_mfa_policy.multiple_factors_enabled?
end
diff --git a/app/views/shared/_fallback_links.html.erb b/app/views/shared/_fallback_links.html.erb
deleted file mode 100644
index b24bc11988b..00000000000
--- a/app/views/shared/_fallback_links.html.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- <%= @presenter.fallback_question %>
-
- <% if @presenter.link_path %>
- <%= link_to(@presenter.link_text, @presenter.link_path) %>
- <% end %>
-
diff --git a/app/views/two_factor_authentication/_troubleshooting_options.html.erb b/app/views/two_factor_authentication/_troubleshooting_options.html.erb
new file mode 100644
index 00000000000..5faa16cf596
--- /dev/null
+++ b/app/views/two_factor_authentication/_troubleshooting_options.html.erb
@@ -0,0 +1,3 @@
+<%= render TroubleshootingOptionsComponent.new(options: @presenter.troubleshooting_options) do |c| %>
+ <% c.with_header { t('components.troubleshooting_options.default_heading') } %>
+<% end %>
diff --git a/app/views/two_factor_authentication/backup_code_verification/show.html.erb b/app/views/two_factor_authentication/backup_code_verification/show.html.erb
index 9626d5e4345..f717229662e 100644
--- a/app/views/two_factor_authentication/backup_code_verification/show.html.erb
+++ b/app/views/two_factor_authentication/backup_code_verification/show.html.erb
@@ -25,5 +25,5 @@
<%= f.submit t('forms.buttons.submit.default'), class: 'display-block margin-y-5' %>
<% end %>
-<%= render 'shared/fallback_links', presenter: @presenter %>
+<%= render 'two_factor_authentication/troubleshooting_options', presenter: @presenter %>
<%= render 'shared/cancel', link: sign_out_path %>
diff --git a/app/views/two_factor_authentication/otp_verification/show.html.erb b/app/views/two_factor_authentication/otp_verification/show.html.erb
index 0211287c69d..fed58b72d88 100644
--- a/app/views/two_factor_authentication/otp_verification/show.html.erb
+++ b/app/views/two_factor_authentication/otp_verification/show.html.erb
@@ -61,12 +61,7 @@
).with_content(t('links.two_factor_authentication.send_another_code')) %>
<% end %>
-
-<%= render(
- 'shared/troubleshooting_options',
- heading: @presenter.troubleshooting_header,
- options: @presenter.troubleshooting_options,
- ) %>
+<%= render 'two_factor_authentication/troubleshooting_options', presenter: @presenter %>
<% if MfaPolicy.new(current_user).two_factor_enabled? %>
<%= render 'shared/cancel', link: @presenter.cancel_link %>
diff --git a/app/views/two_factor_authentication/personal_key_verification/show.html.erb b/app/views/two_factor_authentication/personal_key_verification/show.html.erb
index 0e15b428285..9d564407d71 100644
--- a/app/views/two_factor_authentication/personal_key_verification/show.html.erb
+++ b/app/views/two_factor_authentication/personal_key_verification/show.html.erb
@@ -14,5 +14,5 @@
<%= f.submit t('forms.buttons.submit.default'), class: 'display-block margin-y-5' %>
<% end %>
-<%= render 'shared/fallback_links', presenter: @presenter %>
+<%= render 'two_factor_authentication/troubleshooting_options', presenter: @presenter %>
<%= render 'shared/cancel', link: sign_out_path %>
diff --git a/app/views/two_factor_authentication/piv_cac_verification/show.html.erb b/app/views/two_factor_authentication/piv_cac_verification/show.html.erb
index 81d64fa01d1..b1938bb4afb 100644
--- a/app/views/two_factor_authentication/piv_cac_verification/show.html.erb
+++ b/app/views/two_factor_authentication/piv_cac_verification/show.html.erb
@@ -15,5 +15,6 @@
wide: true,
).with_content(@presenter.piv_cac_capture_text) %>
-<%= render 'shared/fallback_links', presenter: @presenter %>
+
+<%= render 'two_factor_authentication/troubleshooting_options', presenter: @presenter %>
<%= render 'shared/cancel', link: @presenter.cancel_link %>
diff --git a/app/views/two_factor_authentication/totp_verification/show.html.erb b/app/views/two_factor_authentication/totp_verification/show.html.erb
index 6285af8c435..23faf7f04b6 100644
--- a/app/views/two_factor_authentication/totp_verification/show.html.erb
+++ b/app/views/two_factor_authentication/totp_verification/show.html.erb
@@ -33,5 +33,5 @@
) %>
-<%= render 'shared/fallback_links', presenter: @presenter %>
+<%= render 'two_factor_authentication/troubleshooting_options', presenter: @presenter %>
<%= render 'shared/cancel', link: @presenter.cancel_link %>
diff --git a/app/views/two_factor_authentication/webauthn_verification/show.html.erb b/app/views/two_factor_authentication/webauthn_verification/show.html.erb
index 4da63216a14..72e5c774e33 100644
--- a/app/views/two_factor_authentication/webauthn_verification/show.html.erb
+++ b/app/views/two_factor_authentication/webauthn_verification/show.html.erb
@@ -33,31 +33,6 @@
},
) %>
- <%= render TroubleshootingOptionsComponent.new do |c| %>
- <% c.with_header { t('components.troubleshooting_options.default_heading') } %>
- <% if @presenter.link_path.present? %>
- <% c.with_option(url: @presenter.link_path).with_content(@presenter.link_text) %>
- <% end %>
- <% if @presenter.platform_authenticator? %>
- <% c.with_option(
- url: help_center_redirect_path(
- category: 'trouble-signing-in',
- article: 'face-or-touch-unlock',
- flow: :two_factor_authentication,
- step: :webauthn_verification,
- ),
- new_tab: true,
- ).with_content(t('instructions.mfa.webauthn_platform.learn_more_help')) %>
- <% end %>
- <% c.with_option(
- url: help_center_redirect_path(
- category: 'get-started',
- article: 'authentication-options',
- flow: :two_factor_authentication,
- step: :webauthn_verification,
- ),
- new_tab: true,
- ).with_content(t('two_factor_authentication.learn_more')) %>
- <% end %>
+ <%= render 'two_factor_authentication/troubleshooting_options', presenter: @presenter %>
<% end %>
<%= render 'shared/cancel', link: @presenter.cancel_link %>
diff --git a/config/locales/two_factor_authentication/en.yml b/config/locales/two_factor_authentication/en.yml
index b059550795c..047133f4f0e 100644
--- a/config/locales/two_factor_authentication/en.yml
+++ b/config/locales/two_factor_authentication/en.yml
@@ -14,8 +14,6 @@ en:
attempt_remaining_warning_html:
one: You have %{count} attempt remaining.
other: You have %{count} attempts remaining.
- backup_code_fallback:
- question: Don’t have your backup codes available?
backup_code_header_text: Enter your backup security code
backup_code_prompt: You can use this security code once. After you enter it,
you’ll need to use a new key.
@@ -111,8 +109,6 @@ en:
a default phone number.
one_number_title: This is your default number
title: Make this your default phone number?
- personal_key_fallback:
- question: Don’t have your personal key?
personal_key_header_text: Enter your personal key
personal_key_prompt: You can use this personal key once. After you enter it,
you’ll be provided a new key.
@@ -120,8 +116,6 @@ en:
delete:
failure: Unable to remove your phone.
success: Your phone has been removed.
- phone_fallback:
- question: Can’t use your phone?
phone_fee_disclosure: Message and data rates may apply.
phone_info: We’ll send you a one-time code each time you sign in.
phone_label: Phone number
@@ -129,8 +123,6 @@ en:
troubleshooting:
change_number: Use another phone number
code_not_received: I didn’t receive my one-time code
- piv_cac_fallback:
- question: Don’t have your PIV or CAC available?
piv_cac_header_text: Present your PIV/CAC
piv_cac_webauthn_available: Use your security key
please_try_again_html: Please try again in %{countdown}.
@@ -142,8 +134,6 @@ en:
google_policy_link: Privacy Policy
google_tos_link: Terms of Service
login_tos_link: Mobile Terms of Use
- totp_fallback:
- question: Don’t have your authenticator app?
totp_header_text: Enter your authentication app code
two_factor_aal3_choice: Additional authentication required
two_factor_aal3_choice_intro: This app requires a higher level of security. You
diff --git a/config/locales/two_factor_authentication/es.yml b/config/locales/two_factor_authentication/es.yml
index 35dd7722654..86482e50eb4 100644
--- a/config/locales/two_factor_authentication/es.yml
+++ b/config/locales/two_factor_authentication/es.yml
@@ -14,8 +14,6 @@ es:
attempt_remaining_warning_html:
one: Le quedan %{count} intento.
other: Le quedan %{count} intentos.
- backup_code_fallback:
- question: '¿No tienes tus códigos de seguridad disponibles?'
backup_code_header_text: Ingrese su código de seguridad de respaldo
backup_code_prompt: Puede utilizar este código de seguridad una vez. Después de
ingresarlo, deberá usar una nueva clave.
@@ -119,8 +117,6 @@ es:
seleccionar un número de teléfono predeterminado.
one_number_title: Este es tu número predeterminado
title: '¿Es este tu número de teléfono predeterminado?'
- personal_key_fallback:
- question: '¿No tiene su clave personal?'
personal_key_header_text: Ingrese su clave personal
personal_key_prompt: Puede usar esta clave personal una vez. Después de
ingresarlo, se le dará una nueva clave.
@@ -128,8 +124,6 @@ es:
delete:
failure: No se puede eliminar el teléfono.
success: Su teléfono ha sido eliminado.
- phone_fallback:
- question: '¿No puede utilizar su teléfono?'
phone_fee_disclosure: Se pueden aplicar tarifas por mensajes y datos.
phone_info: Le enviaremos un código de un solo uso cada vez que ingrese.
phone_label: Número de teléfono
@@ -137,8 +131,6 @@ es:
troubleshooting:
change_number: Utilice otro número de teléfono.
code_not_received: No recibí mi código de un solo uso.
- piv_cac_fallback:
- question: '¿No tienes su PIV o CAC disponibles?'
piv_cac_header_text: Presenta tu PIV/CAC
piv_cac_webauthn_available: Utilice su llave de seguridad
please_try_again_html: Inténtelo de nuevo en %{countdown}.
@@ -150,8 +142,6 @@ es:
google_policy_link: aplican la política de privacidad
google_tos_link: las condiciones de servicio
login_tos_link: las condiciones de uso
- totp_fallback:
- question: '¿No tiene su aplicación de autenticación?'
totp_header_text: Ingrese su código de la app de autenticación
two_factor_aal3_choice: Se requiere autenticación adicional
two_factor_aal3_choice_intro: Esta aplicación requiere un mayor nivel de
diff --git a/config/locales/two_factor_authentication/fr.yml b/config/locales/two_factor_authentication/fr.yml
index 11d35425dd5..8701f807602 100644
--- a/config/locales/two_factor_authentication/fr.yml
+++ b/config/locales/two_factor_authentication/fr.yml
@@ -14,8 +14,6 @@ fr:
attempt_remaining_warning_html:
one: Il vous reste %{count} tentative.
other: Il vous reste %{count} tentatives.
- backup_code_fallback:
- question: Vous n’avez pas vos codes de secours disponibles?
backup_code_header_text: Entrez votre code de sécurité de secours
backup_code_prompt: Vous pouvez utiliser ce code de sécurité une fois. Après
l’avoir entré, vous devrez utiliser une nouvelle clé.
@@ -124,8 +122,6 @@ fr:
pour pouvoir sélectionner un numéro de téléphone par défaut.
one_number_title: Il s’agit de votre numéro par défaut
title: Faites-en votre numéro de téléphone par défaut?
- personal_key_fallback:
- question: Vous n’avez pas votre clé personnelle?
personal_key_header_text: Entrez votre clé personnelle
personal_key_prompt: Vous pouvez utiliser cette clé personnelle une fois
seulement. Une fois que vous l’entrez, vous recevrez une nouvelle clé.
@@ -133,8 +129,6 @@ fr:
delete:
failure: Impossible de supprimer votre numéro de téléphone.
success: Votre numéro de téléphone a été supprimé.
- phone_fallback:
- question: Vous ne pouvez pas utiliser votre téléphone?
phone_fee_disclosure: Des messages et débits de données peuvent être appliqués.
phone_info: Nous vous enverrons un code à usage unique à chaque fois que vous
vous connecterez.
@@ -143,8 +137,6 @@ fr:
troubleshooting:
change_number: Utilisez un autre numéro de téléphone
code_not_received: Je n’ai pas reçu mon code à usage unique
- piv_cac_fallback:
- question: Votre PIV ou CAC n’est pas disponible?
piv_cac_header_text: Veuillez présenter votre carte PIV/CAC
piv_cac_webauthn_available: Utilisez votre clé de sécurité
please_try_again_html: Veuillez essayer de nouveau dans %{countdown}.
@@ -157,8 +149,6 @@ fr:
google_policy_link: règles de confidentialité
google_tos_link: conditions de service
login_tos_link: conditions d’utilisation
- totp_fallback:
- question: Vous n’avez pas votre application d’authentification?
totp_header_text: Entrez votre code d’application d’authentification
two_factor_aal3_choice: Authentification supplémentaire requise
two_factor_aal3_choice_intro: Cette application nécessite un niveau de sécurité
diff --git a/spec/components/troubleshooting_options_component_spec.rb b/spec/components/troubleshooting_options_component_spec.rb
index 15dfb5021f4..180d8978c22 100644
--- a/spec/components/troubleshooting_options_component_spec.rb
+++ b/spec/components/troubleshooting_options_component_spec.rb
@@ -10,11 +10,26 @@
context 'with options' do
it 'renders troubleshooting options' do
rendered = render_inline(TroubleshootingOptionsComponent.new) do |c|
- c.with_option(url: '/').with_content('Link Text')
+ c.with_option(url: '/1').with_content('Link Text 1')
+ c.with_option(url: '/2') { 'Link Text 2' }
end
expect(rendered).to have_css('.troubleshooting-options')
- expect(rendered).to have_link('Link Text', href: '/')
+ expect(rendered).to have_link('Link Text 1', href: '/1')
+ expect(rendered).to have_link('Link Text 2', href: '/2')
+ end
+
+ context 'with options specified by constructor' do
+ it 'renders troubleshooting options' do
+ rendered = render_inline(
+ TroubleshootingOptionsComponent.new(
+ options: [BlockLinkComponent.new(url: '/').with_content('Link Text')],
+ ),
+ )
+
+ expect(rendered).to have_css('.troubleshooting-options')
+ expect(rendered).to have_link('Link Text', href: '/')
+ end
end
context 'with tag options' do
diff --git a/spec/features/two_factor_authentication/sign_in_spec.rb b/spec/features/two_factor_authentication/sign_in_spec.rb
index 20d9ba7ab64..0e8338fff64 100644
--- a/spec/features/two_factor_authentication/sign_in_spec.rb
+++ b/spec/features/two_factor_authentication/sign_in_spec.rb
@@ -413,15 +413,6 @@ def attempt_to_bypass_2fa
end
end
- describe 'when the user is not piv/cac enabled' do
- it 'has no link to piv/cac during login' do
- user = create(:user, :fully_registered)
- sign_in_before_2fa(user)
-
- expect(page).not_to have_link(t('two_factor_authentication.piv_cac_fallback.question'))
- end
- end
-
describe 'when the user is TOTP enabled and phone enabled' do
it 'allows SMS and Voice fallbacks' do
user = create(:user, :with_authentication_app, :with_phone)
diff --git a/spec/features/users/sign_in_spec.rb b/spec/features/users/sign_in_spec.rb
index 6cc534dbbb7..615be823875 100644
--- a/spec/features/users/sign_in_spec.rb
+++ b/spec/features/users/sign_in_spec.rb
@@ -761,11 +761,6 @@
expect(page).to have_current_path login_two_factor_authenticator_path
end
-
- it 'does not display OTP Fallback text and links' do
- expect(page).
- to_not have_content t('two_factor_authentication.phone_fallback.question')
- end
end
context 'visiting via SP1, then via SP2, then signing in' do
diff --git a/spec/features/users/sign_up_spec.rb b/spec/features/users/sign_up_spec.rb
index 41eb12c4753..c47b196bfbb 100644
--- a/spec/features/users/sign_up_spec.rb
+++ b/spec/features/users/sign_up_spec.rb
@@ -404,14 +404,8 @@ def clipboard_text
visit_idp_from_oidc_sp_with_hspd12_and_require_piv_cac
sign_up_and_set_password
- expect(page).to_not have_selector('#two_factor_options_form_selection_phone', count: 1)
- expect(page).to_not have_selector('#two_factor_options_form_selection_webauthn', count: 1)
- expect(page).to_not have_selector('#two_factor_options_form_selection_auth_app', count: 1)
- expect(page).to_not have_selector('#two_factor_options_form_selection_backup_code', count: 1)
- expect(page).to have_selector('#two_factor_options_form_selection_piv_cac', count: 1)
-
- select_2fa_option('piv_cac')
- expect(page).to_not have_content(t('two_factor_authentication.piv_cac_fallback.question'))
+ expect(page).to have_field('two_factor_options_form[selection][]', count: 1)
+ expect(page).to have_field(t('two_factor_authentication.two_factor_choice_options.piv_cac'))
end
it 'allows a user to sign up with backup codes and add methods after without reauthentication' do
diff --git a/spec/presenters/two_factor_auth_code/authenticator_delivery_presenter_spec.rb b/spec/presenters/two_factor_auth_code/authenticator_delivery_presenter_spec.rb
index 8a6420590e2..fae72c6853d 100644
--- a/spec/presenters/two_factor_auth_code/authenticator_delivery_presenter_spec.rb
+++ b/spec/presenters/two_factor_auth_code/authenticator_delivery_presenter_spec.rb
@@ -13,13 +13,6 @@
end
end
- describe '#fallback_question' do
- it 'supplies a fallback_question' do
- expect(presenter.fallback_question).to \
- eq(t('two_factor_authentication.totp_fallback.question'))
- end
- end
-
it 'handles multiple locales' do
I18n.available_locales.each do |locale|
I18n.locale = locale
diff --git a/spec/presenters/two_factor_auth_code/backup_code_presenter_spec.rb b/spec/presenters/two_factor_auth_code/backup_code_presenter_spec.rb
index 4206a4eb27f..0d27ec33362 100644
--- a/spec/presenters/two_factor_auth_code/backup_code_presenter_spec.rb
+++ b/spec/presenters/two_factor_auth_code/backup_code_presenter_spec.rb
@@ -9,13 +9,6 @@
TwoFactorAuthCode::BackupCodePresenter.new(data: arguments, view: view, service_provider: nil)
end
- describe '#fallback_question' do
- it 'returns the fallback question' do
- expect(presenter.fallback_question).to eq \
- t('two_factor_authentication.backup_code_fallback.question')
- end
- end
-
describe '#cancel_link' do
it 'returns the link for cancellation' do
expect(presenter.cancel_link).to eq \
diff --git a/spec/presenters/two_factor_auth_code/generic_delivery_presenter_spec.rb b/spec/presenters/two_factor_auth_code/generic_delivery_presenter_spec.rb
index 23aecc25140..348c8621eeb 100644
--- a/spec/presenters/two_factor_auth_code/generic_delivery_presenter_spec.rb
+++ b/spec/presenters/two_factor_auth_code/generic_delivery_presenter_spec.rb
@@ -3,14 +3,46 @@
RSpec.describe TwoFactorAuthCode::GenericDeliveryPresenter do
include Rails.application.routes.url_helpers
- it 'is an abstract presenter with methods that should be implemented' do
- presenter = presenter_with
+ let(:presenter) { presenter_with }
- %w[header fallback_links].each do |m|
+ it 'is an abstract presenter with methods that should be implemented' do
+ %w[header].each do |m|
expect { presenter.send(m.to_sym) }.to raise_error(NotImplementedError)
end
end
+ describe '#troubleshooting_options' do
+ it 'includes default troubleshooting options' do
+ expect(presenter.troubleshooting_options.size).to eq(2)
+ expect(presenter.troubleshooting_options[0]).to satisfy do |c|
+ c.url == login_two_factor_options_path &&
+ c.content == t('two_factor_authentication.login_options_link_text')
+ end
+ expect(presenter.troubleshooting_options[1]).to satisfy do |c|
+ c.content == t('two_factor_authentication.learn_more') && c.new_tab?
+ end
+ end
+
+ context 'with blank link path' do
+ before do
+ allow(presenter).to receive(:link_path).and_return('')
+ end
+
+ it 'includes default troubleshooting options' do
+ expect(presenter.troubleshooting_options.size).to eq(1)
+ expect(presenter.troubleshooting_options[0]).to satisfy do |c|
+ c.new_tab? &&
+ c.content == t('two_factor_authentication.learn_more') &&
+ c.url == help_center_redirect_path(
+ category: 'get-started',
+ article: 'authentication-options',
+ flow: :two_factor_authentication,
+ )
+ end
+ end
+ end
+ end
+
def presenter_with(arguments = {}, view = ActionController::Base.new.view_context)
TwoFactorAuthCode::GenericDeliveryPresenter.new(
data: arguments,
diff --git a/spec/presenters/two_factor_auth_code/personal_key_presenter_spec.rb b/spec/presenters/two_factor_auth_code/personal_key_presenter_spec.rb
deleted file mode 100644
index 9dcad3d0870..00000000000
--- a/spec/presenters/two_factor_auth_code/personal_key_presenter_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe TwoFactorAuthCode::PersonalKeyPresenter do
- include Rails.application.routes.url_helpers
-
- let(:presenter) do
- TwoFactorAuthCode::PersonalKeyPresenter.new
- end
-
- describe '#fallback_question' do
- it 'returns the fallback question' do
- expect(presenter.fallback_question).to eq \
- t('two_factor_authentication.personal_key_fallback.question')
- end
- end
-end
diff --git a/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb b/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb
index b0a7b983c28..76c76dc30c2 100644
--- a/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb
+++ b/spec/presenters/two_factor_auth_code/phone_delivery_presenter_spec.rb
@@ -58,14 +58,28 @@
end
describe '#troubleshooting_options' do
+ it { expect(presenter.troubleshooting_options.size).to eq(3) }
+
+ it 'includes a link to troubleshoot a missing one-time code' do
+ expect(presenter.troubleshooting_options).to include(
+ an_object_satisfying do |c|
+ c.content == t(
+ 'two_factor_authentication.phone_verification.troubleshooting.code_not_received',
+ )
+ end,
+ )
+ end
+
context 'when phone is unconfirmed' do
let(:unconfirmed_phone) { true }
it 'should show an option to change phone number' do
expect(presenter.troubleshooting_options).to include(
- {
- url: add_phone_path,
- text: t('two_factor_authentication.phone_verification.troubleshooting.change_number'),
- },
+ an_object_satisfying do |c|
+ c.url == add_phone_path &&
+ c.content == t(
+ 'two_factor_authentication.phone_verification.troubleshooting.change_number',
+ )
+ end,
)
end
end
@@ -73,10 +87,10 @@
context 'when phone is confirmed' do
it 'shpould show an option to show to mfa options list' do
expect(presenter.troubleshooting_options).to include(
- {
- url: login_two_factor_options_path,
- text: t('two_factor_authentication.login_options_link_text'),
- },
+ an_object_satisfying do |c|
+ c.url == login_two_factor_options_path &&
+ c.content == t('two_factor_authentication.login_options_link_text')
+ end,
)
end
end
diff --git a/spec/presenters/two_factor_auth_code/piv_cac_authentication_presenter_spec.rb b/spec/presenters/two_factor_auth_code/piv_cac_authentication_presenter_spec.rb
index 936f16b3b51..55b49b5be80 100644
--- a/spec/presenters/two_factor_auth_code/piv_cac_authentication_presenter_spec.rb
+++ b/spec/presenters/two_factor_auth_code/piv_cac_authentication_presenter_spec.rb
@@ -110,23 +110,36 @@
end
end
- describe '#fallback_question' do
+ describe '#troubleshooting_options' do
context 'when the user can switch to a different method' do
let(:allow_user_to_switch_method) { true }
+ let(:phishing_resistant_required) { false }
- it 'returns a question about switching methods' do
- expect(presenter.fallback_question).to eq(
- t('two_factor_authentication.piv_cac_fallback.question'),
- )
+ it 'includes option to choose another authentication method' do
+ expect(presenter.troubleshooting_options.size).to eq(2)
+ expect(presenter.troubleshooting_options.first).to satisfy do |c|
+ c.url == login_two_factor_options_path &&
+ c.content == t('two_factor_authentication.login_options_link_text')
+ end
+ end
+
+ context 'when phishing resistant method is required' do
+ let(:phishing_resistant_required) { true }
+
+ it 'includes option to use another phishing resistant method' do
+ expect(presenter.troubleshooting_options.size).to eq(2)
+ expect(presenter.troubleshooting_options.first).to satisfy do |c|
+ c.url == login_two_factor_webauthn_url &&
+ c.content == t('two_factor_authentication.piv_cac_webauthn_available')
+ end
+ end
end
end
context 'when the user cannot switch to a different method' do
let(:allow_user_to_switch_method) { false }
- it 'returns an empty string' do
- expect(presenter.fallback_question).to eq('')
- end
+ it { expect(presenter.troubleshooting_options.size).to eq(1) }
end
end
diff --git a/spec/presenters/two_factor_auth_code/webauthn_authentication_presenter_spec.rb b/spec/presenters/two_factor_auth_code/webauthn_authentication_presenter_spec.rb
index 3a4ab64a054..51f6cadf39a 100644
--- a/spec/presenters/two_factor_auth_code/webauthn_authentication_presenter_spec.rb
+++ b/spec/presenters/two_factor_auth_code/webauthn_authentication_presenter_spec.rb
@@ -155,6 +155,51 @@
end
end
+ describe '#troubleshooting_options' do
+ context 'when the user can switch to a different method' do
+ let(:allow_user_to_switch_method) { true }
+ let(:phishing_resistant_required) { false }
+
+ it 'includes option to choose another authentication method' do
+ expect(presenter.troubleshooting_options.size).to eq(2)
+ expect(presenter.troubleshooting_options.first).to satisfy do |c|
+ c.url == login_two_factor_options_path &&
+ c.content == t('two_factor_authentication.login_options_link_text')
+ end
+ end
+
+ context 'with platform authenticator' do
+ let(:platform_authenticator) { true }
+
+ it 'includes option to learn more about face or touch unlock' do
+ expect(presenter.troubleshooting_options.size).to eq(3)
+ expect(presenter.troubleshooting_options[1]).to satisfy do |c|
+ c.content == t('instructions.mfa.webauthn_platform.learn_more_help')
+ end
+ end
+ end
+
+ context 'when phishing resistant method is required' do
+ let(:phishing_resistant_required) { true }
+
+ it 'includes option to use another phishing resistant method' do
+ expect(presenter.troubleshooting_options.size).to eq(2)
+ expect(presenter.troubleshooting_options.first).to satisfy do |c|
+ c.url == login_two_factor_piv_cac_url &&
+ c.content == t('two_factor_authentication.webauthn_piv_available')
+ end
+ end
+ end
+ end
+
+ context 'when the user cannot switch to a different method' do
+ let(:allow_user_to_switch_method) { false }
+ let(:phishing_resistant_required) { true }
+
+ it { expect(presenter.troubleshooting_options.size).to eq(1) }
+ end
+ end
+
describe '#cancel_link' do
let(:locale) { LinkLocaleResolver.locale }