diff --git a/app/controllers/analytics_controller.rb b/app/controllers/analytics_controller.rb index 4ec79522f56..8aab1da92d0 100644 --- a/app/controllers/analytics_controller.rb +++ b/app/controllers/analytics_controller.rb @@ -3,9 +3,10 @@ class AnalyticsController < ApplicationController before_action :confirm_two_factor_authenticated def create - unless analytics_saved? - session[:platform_authenticator] = true - analytics.track_event(Analytics::PLATFORM_AUTHENTICATOR, results.to_h) + results.each do |event, result| + next if result.nil? + + analytics.track_event(event, result.to_h) end head :ok end @@ -13,11 +14,28 @@ def create private def results - FormResponse.new(success: true, errors: {}, - extra: { platform_authenticator: params[:available] }) + { + Analytics::FRONTEND_BROWSER_CAPABILITIES => platform_authenticator_result, + } + end + + def platform_authenticator_result + return if platform_authenticator_results_saved? || !platform_authenticator_params_valid? + + session[:platform_authenticator_analytics_saved] = true + platform_authenticator_available = params[:available] || + params.dig(:platform_authenticator, :available) + extra = { platform_authenticator: (platform_authenticator_available == 'true') } + FormResponse.new(success: true, errors: {}, extra: extra) + end + + def platform_authenticator_params_valid? + result = params[:available] || params.dig(:platform_authenticator, :available) + %w[true false].include?(result) end - def analytics_saved? - session[:platform_authenticator] + def platform_authenticator_results_saved? + session[:platform_authenticator_analytics_saved] == true || + session[:platform_authenticator] == true end end diff --git a/app/javascript/app/platform-authenticator.js b/app/javascript/app/platform-authenticator.js index 7ea97674db0..4131e33be9c 100644 --- a/app/javascript/app/platform-authenticator.js +++ b/app/javascript/app/platform-authenticator.js @@ -6,7 +6,7 @@ function platformAuthenticator() { const xhr = new XMLHttpRequest(); xhr.open('POST', '/analytics', true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - xhr.send(`available=${userIntent}`); + xhr.send(`platform_authenticator[available]=${userIntent}`); }); } } diff --git a/app/services/analytics.rb b/app/services/analytics.rb index a4215775d61..fbd816d7d66 100644 --- a/app/services/analytics.rb +++ b/app/services/analytics.rb @@ -61,6 +61,7 @@ def browser DOC_AUTH = 'Doc Auth'.freeze # visited or submitted is appended EMAIL_AND_PASSWORD_AUTH = 'Email and Password Authentication'.freeze EMAIL_CHANGE_REQUEST = 'Email Change Request'.freeze + FRONTEND_BROWSER_CAPABILITIES = 'Frontend: Browser capabilities'.freeze IDV_BASIC_INFO_VISIT = 'IdV: basic info visited'.freeze IDV_BASIC_INFO_SUBMITTED_FORM = 'IdV: basic info form submitted'.freeze IDV_BASIC_INFO_SUBMITTED_VENDOR = 'IdV: basic info vendor submitted'.freeze @@ -113,7 +114,6 @@ def browser PERSONAL_KEY_VIEWED = 'Personal Key Viewed'.freeze PHONE_CHANGE_REQUESTED = 'Phone Number Change: requested'.freeze PHONE_DELETION_REQUESTED = 'Phone Number Deletion: requested'.freeze - PLATFORM_AUTHENTICATOR = 'Platform Authenticator'.freeze PROFILE_ENCRYPTION_INVALID = 'Profile Encryption: Invalid'.freeze PROFILE_PERSONAL_KEY_CREATE = 'Profile: Created new personal key'.freeze RATE_LIMIT_TRIGGERED = 'Rate Limit Triggered'.freeze diff --git a/spec/controllers/analytics_controller_spec.rb b/spec/controllers/analytics_controller_spec.rb deleted file mode 100644 index 982f0555dc8..00000000000 --- a/spec/controllers/analytics_controller_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'rails_helper' - -describe AnalyticsController do - describe '#create' do - before do - stub_sign_in - stub_analytics - end - - it 'logs true for platform authenticator' do - expect(@analytics).to receive(:track_event). - with(Analytics::PLATFORM_AUTHENTICATOR, - errors: {}, success: true, platform_authenticator: 'true') - - post :create, params: { available: true } - end - - it 'logs false for platform authenticator' do - expect(@analytics).to receive(:track_event). - with(Analytics::PLATFORM_AUTHENTICATOR, - errors: {}, success: true, platform_authenticator: 'false') - - post :create, params: { available: false } - end - - it 'logs once per session' do - expect(@analytics).to receive(:track_event). - with(Analytics::PLATFORM_AUTHENTICATOR, - errors: {}, success: true, platform_authenticator: 'true') - - post :create, params: { available: true } - post :create, params: { available: true } - end - end -end diff --git a/spec/requests/edit_user_spec.rb b/spec/requests/edit_user_spec.rb index 057c8ed7894..a877e5185af 100644 --- a/spec/requests/edit_user_spec.rb +++ b/spec/requests/edit_user_spec.rb @@ -10,18 +10,9 @@ def user_session session['warden.user.user.session'] end - def sign_in_as_a_valid_user - post new_user_session_path, params: { user: { email: user.email, password: user.password } } - get otp_send_path, params: { otp_delivery_selection_form: { otp_delivery_preference: 'sms' } } - follow_redirect! - post login_two_factor_path, params: { - otp_delivery_preference: 'sms', code: user.reload.direct_otp - } - end - context 'user changes email address' do before do - sign_in_as_a_valid_user + sign_in_user(user) put manage_email_path, params: { update_user_email_form: { email: 'new_email@example.com' } } end @@ -51,7 +42,7 @@ def sign_in_as_a_valid_user context 'user submits email address with invalid encoding' do it 'returns a 400 error and logs the user uuid' do - sign_in_as_a_valid_user + sign_in_user(user) params = { update_user_email_form: { email: "test\xFFbar\xF8@test.com" } } headers = { CONTENT_TYPE: 'application/x-www-form-urlencoded;foo' } diff --git a/spec/requests/frontend_analytics_spec.rb b/spec/requests/frontend_analytics_spec.rb new file mode 100644 index 00000000000..ab1dbb7d66f --- /dev/null +++ b/spec/requests/frontend_analytics_spec.rb @@ -0,0 +1,72 @@ +require 'rails_helper' + +describe 'frontend analytics requests' do + describe 'platform authenticators' do + let(:analytics) { FakeAnalytics.new } + + before do + allow(analytics).to receive(:track_event) + allow(Analytics).to receive(:new).and_return(analytics) + end + + it 'does not log anything if the user is not authed' do + expect(analytics).to_not receive(:track_event). + with(Analytics::FRONTEND_BROWSER_CAPABILITIES, any_args) + + post analytics_path, params: { platform_authenticator: { available: true } } + end + + it 'logs true if the platform authenticator is available' do + sign_in_user + + post analytics_path, params: { platform_authenticator: { available: true } } + + expect(analytics).to have_received(:track_event). + with(Analytics::FRONTEND_BROWSER_CAPABILITIES, hash_including(platform_authenticator: true)) + end + + it 'logs false if the platform authenticator is not available' do + sign_in_user + + post analytics_path, params: { platform_authenticator: { available: false } } + + expect(analytics).to have_received(:track_event). + with( + Analytics::FRONTEND_BROWSER_CAPABILITIES, + hash_including(platform_authenticator: false) + ) + end + + it 'only logs 1 platform authenticator event per session' do + sign_in_user + + post analytics_path, params: { platform_authenticator: { available: true } } + post analytics_path, params: { platform_authenticator: { available: true } } + + expect(analytics).to have_received(:track_event). + with( + Analytics::FRONTEND_BROWSER_CAPABILITIES, + hash_including(platform_authenticator: true) + ). + once + end + + it 'logs ignores garbage values' do + sign_in_user + + post analytics_path, params: { platform_authenticator: { available: 'blah blah blah' } } + + expect(analytics).to_not have_received(:track_event). + with(Analytics::FRONTEND_BROWSER_CAPABILITIES, any_args) + end + + it 'supports the legacy API format' do + sign_in_user + + post analytics_path, params: { available: true } + + expect(analytics).to have_received(:track_event). + with(Analytics::FRONTEND_BROWSER_CAPABILITIES, hash_including(platform_authenticator: true)) + end + end +end diff --git a/spec/support/request_helper.rb b/spec/support/request_helper.rb new file mode 100644 index 00000000000..1b706bf2606 --- /dev/null +++ b/spec/support/request_helper.rb @@ -0,0 +1,20 @@ +module RequestHelper + VALID_PASSWORD = 'Val!d Pass w0rd'.freeze + + def user_with_2fa + create(:user, :signed_up, with: { phone: '+1 202-555-1212' }, password: VALID_PASSWORD) + end + + def sign_in_user(user = user_with_2fa) + post new_user_session_path, params: { user: { email: user.email, password: user.password } } + get otp_send_path, params: { otp_delivery_selection_form: { otp_delivery_preference: 'sms' } } + follow_redirect! + post login_two_factor_path, params: { + otp_delivery_preference: 'sms', code: user.reload.direct_otp + } + end +end + +RSpec.configure do |config| + config.include RequestHelper, type: :request +end