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 }