diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ff1476b15b1..9885e2afa96 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -256,9 +256,6 @@ def after_sign_in_path_for(_user) return login_add_piv_cac_prompt_url if session[:needs_to_setup_piv_cac_after_sign_in].present? return reactivate_account_url if user_needs_to_reactivate_account? return login_piv_cac_recommended_path if user_recommended_for_piv_cac? - return webauthn_platform_recommended_path if recommend_webauthn_platform_for_sms_user?( - :recommend_for_authentication, - ) return second_mfa_reminder_url if user_needs_second_mfa_reminder? return backup_code_reminder_url if user_needs_backup_code_reminder? return sp_session_request_url_with_updated_params if sp_session.key?(:request_url) diff --git a/app/controllers/concerns/mfa_setup_concern.rb b/app/controllers/concerns/mfa_setup_concern.rb index b08c215e8ce..138a22a5364 100644 --- a/app/controllers/concerns/mfa_setup_concern.rb +++ b/app/controllers/concerns/mfa_setup_concern.rb @@ -2,12 +2,11 @@ module MfaSetupConcern extend ActiveSupport::Concern - include RecommendWebauthnPlatformConcern def next_setup_path if next_setup_choice confirmation_path - elsif recommend_webauthn_platform_for_sms_user?(:recommend_for_account_creation) + elsif recommend_webauthn_platform_for_sms_user? webauthn_platform_recommended_path elsif suggest_second_mfa? auth_method_confirmation_path @@ -129,4 +128,21 @@ def next_setup_choice determine_next_mfa, ) end + + def recommend_webauthn_platform_for_sms_user? + user_session[:platform_authenticator_available] == true && user_has_phone_setup? + end + + def user_set_up_with_sms? + current_user.phone_configurations.any? do |phone_configuration| + phone_configuration.mfa_enabled? && phone_configuration.delivery_preference == 'sms' + end + end + + def user_has_phone_setup? + user_session[:in_account_creation_flow] == true && + mfa_context.enabled_mfa_methods_count == 1 && + mfa_context.phone_configurations.present? && + user_set_up_with_sms? + end end diff --git a/app/controllers/concerns/recommend_webauthn_platform_concern.rb b/app/controllers/concerns/recommend_webauthn_platform_concern.rb deleted file mode 100644 index 2cb9a5fb6d1..00000000000 --- a/app/controllers/concerns/recommend_webauthn_platform_concern.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module RecommendWebauthnPlatformConcern - def recommend_webauthn_platform_for_sms_user?(bucket) - # Only consider for A/B test if: - # 1. Option would be offered for setup - # 2. User is viewing content in English - # 3. Other recommendations have not already been offered (e.g. PIV/CAC for federal emails) - # 4. User selected to setup phone or authenticated with phone - # 5. User has not already set up a platform authenticator - return false if !device_supports_platform_authenticator_setup? - return false if I18n.locale != :en - return false if current_user.webauthn_platform_recommended_dismissed_at? - return false if !user_set_up_or_authenticated_with_phone? - return false if current_user.webauthn_configurations.platform_authenticators.present? - ab_test_bucket(:RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER) == bucket - end - - private - - def device_supports_platform_authenticator_setup? - user_session[:platform_authenticator_available] == true - end - - def in_account_creation_flow? - user_session[:in_account_creation_flow] == true - end - - def user_set_up_or_authenticated_with_phone? - if in_account_creation_flow? - current_user.phone_configurations.any? do |phone_configuration| - phone_configuration.mfa_enabled? && phone_configuration.delivery_preference == 'sms' - end - else - auth_methods_session.auth_events - .pluck(:auth_method) - .include?(TwoFactorAuthenticatable::AuthMethod::SMS) - end - end -end diff --git a/app/controllers/users/webauthn_platform_recommended_controller.rb b/app/controllers/users/webauthn_platform_recommended_controller.rb index aa18b9b6eeb..ceb500504f5 100644 --- a/app/controllers/users/webauthn_platform_recommended_controller.rb +++ b/app/controllers/users/webauthn_platform_recommended_controller.rb @@ -3,7 +3,6 @@ module Users class WebauthnPlatformRecommendedController < ApplicationController include SecureHeadersConcern - include MfaSetupConcern before_action :confirm_two_factor_authenticated before_action :apply_secure_headers_override @@ -15,22 +14,13 @@ def new def create analytics.webauthn_platform_recommended_submitted(opted_to_add: opted_to_add?) - store_webauthn_platform_recommended_in_session if opted_to_add? + user_session[:webauthn_platform_recommended] = true if opted_to_add? current_user.update(webauthn_platform_recommended_dismissed_at: Time.zone.now) redirect_to dismiss_redirect_path end private - def store_webauthn_platform_recommended_in_session - user_session[:webauthn_platform_recommended] = - if in_account_creation_flow? - :account_creation - else - :authentication - end - end - def opted_to_add? params[:add_method].present? end @@ -38,10 +28,8 @@ def opted_to_add? def dismiss_redirect_path if opted_to_add? webauthn_setup_path(platform: true) - elsif in_account_creation_flow? - next_setup_path || after_mfa_setup_path else - after_sign_in_path_for(current_user) + after_mfa_setup_path end end end diff --git a/app/views/users/webauthn_platform_recommended/new.html.erb b/app/views/users/webauthn_platform_recommended/new.html.erb index 98b5d1cd925..c54a6800430 100644 --- a/app/views/users/webauthn_platform_recommended/new.html.erb +++ b/app/views/users/webauthn_platform_recommended/new.html.erb @@ -3,7 +3,8 @@ <%= render StatusPageComponent.new(status: :info, icon: :question) do |c| %> <% c.with_header { t('webauthn_platform_recommended.heading') } %> -

<%= t('webauthn_platform_recommended.description_save_time') %>

+

<%= t('webauthn_platform_recommended.description_security') %>

+

<%= t('webauthn_platform_recommended.upsell') %>

diff --git a/config/locales/en.yml b/config/locales/en.yml index 3ad55a76a35..11dd4528127 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2046,9 +2046,10 @@ vendor_outage.get_updates: Get updates vendor_outage.get_updates_on_status_page: Get updates on our status page vendor_outage.working: We are working to resolve an error webauthn_platform_recommended.cta: Set up face or touch unlock -webauthn_platform_recommended.description_save_time: Save time by using your face, fingerprint, password, or another method to access your account. This method is faster than receiving a one-time code through text or voice message. -webauthn_platform_recommended.heading: Set up face or touch unlock for a quick and easy sign in +webauthn_platform_recommended.description_security: Secure your sign in with face or touch unlock. Use your face, fingerprint, password, or another method to keep your account safer. +webauthn_platform_recommended.heading: Set up face or touch unlock for a more secure sign in webauthn_platform_recommended.skip: Skip +webauthn_platform_recommended.upsell: Face or touch unlock is phishing-resistant and we don’t store any recordings of your face or fingerprint, so your information stays private. webauthn_setup_mismatch.description_undo: Click “Undo” to remove this option. webauthn_setup_mismatch.description.webauthn: We noticed you’re using a security key instead of face or touch unlock. Click “Continue” to use your security key to sign in from now on. webauthn_setup_mismatch.description.webauthn_platform: We noticed you’re using face or touch unlock instead of a security key. Click “Continue” to use face or touch unlock to sign in from now on. diff --git a/config/locales/es.yml b/config/locales/es.yml index 7d407f80268..788d498b35e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -2057,10 +2057,11 @@ vendor_outage.blocked.phone.default: No podemos verificar teléfonos en estos mo vendor_outage.get_updates: Obtenga actualizaciones vendor_outage.get_updates_on_status_page: Obtenga las actualizaciones en nuestra página de estado vendor_outage.working: Estamos trabajando para corregir un error -webauthn_platform_recommended.cta: Set up face or touch unlock -webauthn_platform_recommended.description_save_time: Save time by using your face, fingerprint, password, or another method to access your account. This method is faster than receiving a one-time code through text or voice message. -webauthn_platform_recommended.heading: Set up face or touch unlock for a quick and easy sign in -webauthn_platform_recommended.skip: Skip +webauthn_platform_recommended.cta: Configurar el desbloqueo facial o táctil +webauthn_platform_recommended.description_security: Proteja su inicio de sesión con el desbloqueo facial o táctil. Use su rostro, huella dactilar, contraseña u otro método para mantener su cuenta más segura. +webauthn_platform_recommended.heading: Configuración del desbloqueo facial o táctil para un inicio de sesión más seguro +webauthn_platform_recommended.skip: Omitir +webauthn_platform_recommended.upsell: El desbloqueo facial o táctil está protegido contra el phishing y no almacenamos ninguna grabación de su rostro ni de su huella dactilar, con el fin de resguardar la privacidad de su información. webauthn_setup_mismatch.description_undo: Haga clic en “Deshacer” para quitar esta opción. webauthn_setup_mismatch.description.webauthn: Sabemos que está usando una clave de seguridad en lugar de desbloqueo facial o táctil. Haga clic en “Continuar” para iniciar sesión con su clave de seguridad de aquí en adelante. webauthn_setup_mismatch.description.webauthn_platform: Sabemos que está usando desbloqueo facial o táctil en lugar de una clave de seguridad. Haga clic en “Continuar” para iniciar sesión con desbloqueo facial o táctil de aquí en adelante. diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4d3d7ea24a6..cac8ae158f5 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -2045,10 +2045,11 @@ vendor_outage.blocked.phone.default: Nous ne sommes pas actuellement en mesure d vendor_outage.get_updates: Obtenir les dernières informations vendor_outage.get_updates_on_status_page: Obtenir les dernières informations sur notre page d’état des systèmes. vendor_outage.working: Nous travaillons à la résolution d’une erreur -webauthn_platform_recommended.cta: Set up face or touch unlock -webauthn_platform_recommended.description_save_time: Save time by using your face, fingerprint, password, or another method to access your account. This method is faster than receiving a one-time code through text or voice message. -webauthn_platform_recommended.heading: Set up face or touch unlock for a quick and easy sign in -webauthn_platform_recommended.skip: Skip +webauthn_platform_recommended.cta: Configurer le déverrouillage facial ou tactile +webauthn_platform_recommended.description_security: Save time by using your face, fingerprint, password, or another method to access your account. This method is faster than receiving a one-time code through text or voice message. +webauthn_platform_recommended.heading: Configurar el desbloqueo facial o táctil +webauthn_platform_recommended.skip: Ignorer +webauthn_platform_recommended.upsell: La confidentialité de vos informations est assurée car le déverrouillage par reconnaissance faciale ou tactile résiste à l’hameçonnage et nous ne conservons aucun enregistrement de votre visage ou de vos empreintes digitales. webauthn_setup_mismatch.description_undo: Cliquez sur « Annuler » pour supprimer cette option. webauthn_setup_mismatch.description.webauthn: Nous avons remarqué que vous utilisiez une clé de sécurité au lieu du déverrouillage facial ou tactile. Cliquez sur « Suite » pour utiliser votre clé de sécurité afin de vous connecter à partir de maintenant. webauthn_setup_mismatch.description.webauthn_platform: Nous avons remarqué que vous utilisiez le déverrouillage facial ou tactile au lieu d’une clé de sécurité. Cliquez sur « Suite » pour utiliser le déverrouillage facial ou tactile afin de vous connecter à partir de maintenant. diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 93adc12d980..f294ddf7973 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -2058,10 +2058,11 @@ vendor_outage.blocked.phone.default: 我们目前无法验证电话。请稍后 vendor_outage.get_updates: 获得最新信息 vendor_outage.get_updates_on_status_page: 在我们的状态页面获得最新信息。 vendor_outage.working: 我们正在争取解决错误。 -webauthn_platform_recommended.cta: Set up face or touch unlock -webauthn_platform_recommended.description_save_time: Save time by using your face, fingerprint, password, or another method to access your account. This method is faster than receiving a one-time code through text or voice message. -webauthn_platform_recommended.heading: Set up face or touch unlock for a quick and easy sign in -webauthn_platform_recommended.skip: Skip +webauthn_platform_recommended.cta: 设置人脸或触摸解锁 +webauthn_platform_recommended.description_security: 使用人脸或触摸解锁来保护你的登录。使用你的面孔、指纹、密码或其他方法来使你的帐户更安全。 +webauthn_platform_recommended.heading: 设置人脸或触摸解锁以更安全地登录 +webauthn_platform_recommended.skip: 跳过 +webauthn_platform_recommended.upsell: 人脸或触摸解锁具有防网络钓鱼功能,而且我们不会存储任何人脸或指纹记录,使得你的信息能保持私密。 webauthn_setup_mismatch.description_undo: 点击“撤消”可删除此选项。 webauthn_setup_mismatch.description.webauthn: 我们注意到您正在使用安全密钥而不是人脸或触摸解锁。点击“继续”即可从现在开始使用您的安全密钥登录。 webauthn_setup_mismatch.description.webauthn_platform: 我们注意到您正在使用人脸或触摸解锁,而不是安全密钥。点击“继续”即可从现在开始使用人脸或触摸解锁登录。 diff --git a/spec/controllers/concerns/mfa_setup_concern_spec.rb b/spec/controllers/concerns/mfa_setup_concern_spec.rb index 046184606b4..9369bdc1668 100644 --- a/spec/controllers/concerns/mfa_setup_concern_spec.rb +++ b/spec/controllers/concerns/mfa_setup_concern_spec.rb @@ -6,13 +6,9 @@ end let(:user) { create(:user, :fully_registered) } - let(:recommend_webauthn_platform_for_sms_user) { false } before do stub_sign_in(user) - allow(controller).to receive(:recommend_webauthn_platform_for_sms_user?) - .with(:recommend_for_account_creation) - .and_return(recommend_webauthn_platform_for_sms_user) end describe '#next_setup_path' do @@ -33,14 +29,6 @@ controller.user_session[:mfa_selections] = ['phone'] end - context 'when user is recommended for webauthn platform for sms user' do - let(:recommend_webauthn_platform_for_sms_user) { true } - - it 'returns webauthn platform recommended path' do - expect(next_setup_path).to eq(webauthn_platform_recommended_path) - end - end - context 'when user only set up a single mfa method' do it 'returns second mfa recommended path' do expect(next_setup_path).to eq(auth_method_confirmation_path) @@ -70,6 +58,21 @@ end end + context 'when user is recommended for webauthn platform setup' do + before do + controller.user_session[:mfa_selections] = ['phone'] + controller.user_session[:platform_authenticator_available] = true + controller.user_session[:in_account_creation_flow] = true + end + + let(:user) { create(:user, :fully_registered) } + let(:recommend_webauthn_platform_for_sms_user?) { true } + + it 'redirects to webauthn recommendation screen' do + expect(next_setup_path).to eq(webauthn_platform_recommended_path) + end + end + context 'when user converts from second mfa reminder' do let(:user) { create(:user, :fully_registered, :with_phone, :with_backup_code) } diff --git a/spec/controllers/concerns/recommend_webauthn_platform_concern_spec.rb b/spec/controllers/concerns/recommend_webauthn_platform_concern_spec.rb deleted file mode 100644 index 7b240292679..00000000000 --- a/spec/controllers/concerns/recommend_webauthn_platform_concern_spec.rb +++ /dev/null @@ -1,181 +0,0 @@ -require 'rails_helper' - -RSpec.describe RecommendWebauthnPlatformConcern do - controller ApplicationController do - include RecommendWebauthnPlatformConcern - end - - let(:user) { create(:user, :fully_registered) } - let(:platform_authenticator_available) { false } - let(:in_account_creation_flow) { false } - - before do - stub_sign_in(user) - controller.user_session[:platform_authenticator_available] = platform_authenticator_available - controller.user_session[:in_account_creation_flow] = in_account_creation_flow - end - - describe '#recommend_webauthn_platform_for_sms_user?' do - let(:bucket) { :recommend_for_account_creation } - - subject(:recommend_webauthn_platform_for_sms_user?) do - controller.recommend_webauthn_platform_for_sms_user?(bucket) - end - - context 'device is not supported for platform authenticator setup' do - let(:platform_authenticator_available) { false } - - it { is_expected.to eq(false) } - end - - context 'device is supported for platform authenticator setup' do - let(:platform_authenticator_available) { true } - - context 'locale is anything other than english' do - before do - I18n.locale = (I18n.available_locales - [:en]).sample - end - - it { is_expected.to eq(false) } - end - - context 'locale is english' do - before do - I18n.locale = :en - end - - context 'user was already recommended for setup' do - let(:user) do - create( - :user, - :fully_registered, - webauthn_platform_recommended_dismissed_at: 2.minutes.ago, - ) - end - - it { is_expected.to eq(false) } - end - - context 'user has not yet been recommended for setup' do - let(:user) { create(:user, :fully_registered) } - - context 'user is in authentication flow' do - let(:in_account_creation_flow) { false } - - context 'user authenticated with an mfa method other than sms' do - before do - controller.auth_methods_session.auth_events.clear - controller.auth_methods_session.authenticate!( - TwoFactorAuthenticatable::AuthMethod::VOICE, - ) - end - - it { is_expected.to eq(false) } - end - - context 'user authenticated with sms' do - before do - controller.auth_methods_session.auth_events.clear - controller.auth_methods_session.authenticate!( - TwoFactorAuthenticatable::AuthMethod::SMS, - ) - end - - context 'user has platform authenticator associated with their account' do - let(:user) { create(:user, :fully_registered, :with_webauthn_platform) } - - it { is_expected.to eq(false) } - end - - context 'user does not have platform authenticator associated with their account' do - let(:user) { create(:user, :fully_registered) } - - context 'user not included in ab test' do - before do - expect(controller).to receive(:ab_test_bucket) - .with(:RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER) - .and_return(nil) - end - - it { is_expected.to eq(false) } - end - - context 'user included in ab test' do - before do - expect(controller).to receive(:ab_test_bucket) - .with(:RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER) - .and_return(bucket) - end - - it { is_expected.to eq(true) } - end - end - end - end - - context 'user is in account creation flow' do - let(:in_account_creation_flow) { true } - - context 'user set up methods not including phone' do - let(:user) { create(:user, :fully_registered, :with_authentication_app) } - - before do - user.phone_configurations.destroy_all - end - - it { is_expected.to eq(false) } - end - - context 'user set up phone as an mfa method' do - let(:user) { create(:user, :fully_registered) } - - context 'user set up phone using voice delivery preference' do - before do - user.phone_configurations.update_all(delivery_preference: :voice) - end - - it { is_expected.to eq(false) } - end - - context 'user set up phone using sms delivery preference' do - before do - user.phone_configurations.update_all(delivery_preference: :sms) - end - - context 'user also set up platform authenticator' do - let(:user) { create(:user, :fully_registered, :with_webauthn_platform) } - - it { is_expected.to eq(false) } - end - - context 'user did not set up platform authenticator' do - let(:user) { create(:user, :fully_registered) } - - context 'user not included in ab test' do - before do - expect(controller).to receive(:ab_test_bucket) - .with(:RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER) - .and_return(nil) - end - - it { is_expected.to eq(false) } - end - - context 'user included in ab test' do - before do - expect(controller).to receive(:ab_test_bucket) - .with(:RECOMMEND_WEBAUTHN_PLATFORM_FOR_SMS_USER) - .and_return(bucket) - end - - it { is_expected.to eq(true) } - end - end - end - end - end - end - end - end - end -end diff --git a/spec/controllers/users/webauthn_platform_recommended_controller_spec.rb b/spec/controllers/users/webauthn_platform_recommended_controller_spec.rb index 34937d03ba1..0ba3fa6ac9e 100644 --- a/spec/controllers/users/webauthn_platform_recommended_controller_spec.rb +++ b/spec/controllers/users/webauthn_platform_recommended_controller_spec.rb @@ -69,12 +69,6 @@ .from(nil) end - it 'redirects user to after sign in path' do - expect(controller).to receive(:after_sign_in_path_for).with(user).and_return(account_path) - - expect(response).to redirect_to(account_path) - end - context 'user is creating account' do before do allow(controller).to receive(:in_account_creation_flow?).and_return(true) @@ -117,19 +111,14 @@ expect(response).to redirect_to(webauthn_setup_path(platform: true)) end - it 'assigns recommended session value to recommendation flow' do - expect { response }.to change { controller.user_session[:webauthn_platform_recommended] } - .from(nil).to(:authentication) - end - context 'user is creating account' do before do allow(controller).to receive(:in_account_creation_flow?).and_return(true) end - it 'assigns recommended session value to recommendation flow' do + it 'adds recommended session value during recommendation flow' do expect { response }.to change { controller.user_session[:webauthn_platform_recommended] } - .from(nil).to(:account_creation) + .from(nil).to(true) end end end diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb index ae57f2198a5..de0d6b227c7 100644 --- a/spec/i18n_spec.rb +++ b/spec/i18n_spec.rb @@ -84,10 +84,6 @@ class BaseTask { key: 'time.formats.event_timestamp', locales: %i[zh] }, { key: 'time.formats.full_date', locales: %i[es] }, # format is the same in Spanish and English { key: 'time.formats.sms_date' }, # for us date format - { key: 'webauthn_platform_recommended.cta' }, # English-only A/B test - { key: 'webauthn_platform_recommended.description_save_time' }, # English-only A/B test - { key: 'webauthn_platform_recommended.heading' }, # English-only A/B test - { key: 'webauthn_platform_recommended.skip' }, # English-only A/B test ].freeze # rubocop:enable Layout/LineLength