diff --git a/app/controllers/concerns/saml_idp_logout_concern.rb b/app/controllers/concerns/saml_idp_logout_concern.rb index 343f00e4934..a6c4d7b0970 100644 --- a/app/controllers/concerns/saml_idp_logout_concern.rb +++ b/app/controllers/concerns/saml_idp_logout_concern.rb @@ -62,6 +62,8 @@ def logout_response def track_logout_event sp_initiated = saml_request.present? + attempts_api_tracker.logout_initiated(success: true) + analytics.logout_initiated( sp_initiated: sp_initiated, oidc: false, diff --git a/app/controllers/openid_connect/authorization_controller.rb b/app/controllers/openid_connect/authorization_controller.rb index 2c07e98e1f1..38416870267 100644 --- a/app/controllers/openid_connect/authorization_controller.rb +++ b/app/controllers/openid_connect/authorization_controller.rb @@ -236,6 +236,8 @@ def track_events acr_values: sp_session[:acr_values], sign_in_duration_seconds:, ) + + attempts_api_tracker.login_completed track_billing_events end diff --git a/app/controllers/openid_connect/logout_controller.rb b/app/controllers/openid_connect/logout_controller.rb index 2db0f22cf3b..ad21471dc3e 100644 --- a/app/controllers/openid_connect/logout_controller.rb +++ b/app/controllers/openid_connect/logout_controller.rb @@ -23,6 +23,10 @@ def show original_method: session[:original_method], ) + attempts_api_tracker.logout_initiated( + success: result.success?, + ) + if result.success? && redirect_uri handle_successful_logout_request(result, redirect_uri) else @@ -46,6 +50,9 @@ def delete redirect_uri = result.extra[:redirect_uri] analytics.oidc_logout_submitted(**to_event(result)) + attempts_api_tracker.logout_initiated( + success: result.success?, + ) if result.success? && redirect_uri handle_logout(result, redirect_uri) diff --git a/app/controllers/saml_idp_controller.rb b/app/controllers/saml_idp_controller.rb index 63d9689e650..4ec6e563e61 100644 --- a/app/controllers/saml_idp_controller.rb +++ b/app/controllers/saml_idp_controller.rb @@ -222,6 +222,8 @@ def track_events acr_values: sp_session[:acr_values], sign_in_duration_seconds:, ) + + attempts_api_tracker.login_completed track_billing_events end diff --git a/app/controllers/sign_out_controller.rb b/app/controllers/sign_out_controller.rb index 038f7a7a924..a0655b75833 100644 --- a/app/controllers/sign_out_controller.rb +++ b/app/controllers/sign_out_controller.rb @@ -5,6 +5,8 @@ class SignOutController < ApplicationController def destroy analytics.logout_initiated(method: 'cancel link') + attempts_api_tracker.logout_initiated(success: true) + url_after_cancellation = decorated_sp_session.cancel_link_url sign_out flash[:success] = t('devise.sessions.signed_out') diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index cef07f24ee2..2b3e408da2b 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -57,6 +57,7 @@ def destroy redirect_to root_path else analytics.logout_initiated(sp_initiated: false, oidc: false) + attempts_api_tracker.logout_initiated(success: true) super end end diff --git a/app/services/attempts_api/tracker_events.rb b/app/services/attempts_api/tracker_events.rb index 789304a155f..12600474f91 100644 --- a/app/services/attempts_api/tracker_events.rb +++ b/app/services/attempts_api/tracker_events.rb @@ -25,12 +25,26 @@ def logged_in_account_purged(success:) # A logged-in user has attempted to change their password def logged_in_password_change(success:, failure_reason: nil) track_event( - :logged_in_password_change, + 'logged-in-password-change', success: success, failure_reason:, ) end + # A user has successfully logged in and is being handed off to integration + def login_completed + track_event('login-completed') + end + + # @param [Boolean] success + # A user has initiated a logout event + def logout_initiated(success:) + track_event( + 'logout-initiated', + success:, + ) + end + # @param [Boolean] success # A user has attempted to enroll the Backup Codes MFA method to their account def mfa_enroll_backup_code(success:) diff --git a/docs/attempts-api/schemas/events/sign-in/LogoutInitiated.yml b/docs/attempts-api/schemas/events/sign-in/LogoutInitiated.yml index 73d5e5692ca..15153f3b125 100644 --- a/docs/attempts-api/schemas/events/sign-in/LogoutInitiated.yml +++ b/docs/attempts-api/schemas/events/sign-in/LogoutInitiated.yml @@ -2,3 +2,8 @@ description: The user has logged out of their account. allOf: - $ref: '../shared/EventProperties.yml' - type: object + properties: + success: + type: boolean + description: | + Indicates whether the logout was successfully initiated \ No newline at end of file diff --git a/spec/controllers/openid_connect/authorization_controller_spec.rb b/spec/controllers/openid_connect/authorization_controller_spec.rb index eeeef7aec9c..abb5a3d337b 100644 --- a/spec/controllers/openid_connect/authorization_controller_spec.rb +++ b/spec/controllers/openid_connect/authorization_controller_spec.rb @@ -112,6 +112,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!(verified_attributes: %w[given_name family_name birthdate]) @@ -166,6 +169,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!(verified_attributes: %w[given_name family_name birthdate]) @@ -261,6 +267,9 @@ it 'tracks IAL2 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 2) user.identities.last.update!( @@ -577,6 +586,9 @@ it 'tracks IAL2 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 2) user.identities.last.update!( @@ -651,6 +663,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!( @@ -727,6 +742,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!( @@ -860,6 +878,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!(verified_attributes: %w[given_name family_name birthdate]) @@ -911,6 +932,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!(verified_attributes: %w[given_name family_name birthdate]) @@ -1007,6 +1031,9 @@ it 'tracks IAL2 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 2) user.identities.last.update!( @@ -1363,6 +1390,9 @@ it 'tracks IAL2 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 2) user.identities.last.update!( @@ -1437,6 +1467,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!( @@ -1513,6 +1546,9 @@ it 'tracks IAL1 authentication event' do travel_to now + 15.seconds stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) IdentityLinker.new(user, service_provider).link_identity(ial: 1) user.identities.last.update!( diff --git a/spec/controllers/openid_connect/logout_controller_spec.rb b/spec/controllers/openid_connect/logout_controller_spec.rb index 86fa260d6c1..8a95ca2e02a 100644 --- a/spec/controllers/openid_connect/logout_controller_spec.rb +++ b/spec/controllers/openid_connect/logout_controller_spec.rb @@ -84,6 +84,9 @@ it 'tracks events' do stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:logout_initiated).with(success: true) action @@ -661,6 +664,9 @@ it 'tracks events' do stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:logout_initiated).with(success: true) action diff --git a/spec/controllers/saml_idp_controller_spec.rb b/spec/controllers/saml_idp_controller_spec.rb index 23e472fda2b..a5af8ae2b59 100644 --- a/spec/controllers/saml_idp_controller_spec.rb +++ b/spec/controllers/saml_idp_controller_spec.rb @@ -46,6 +46,9 @@ it 'tracks the event when idp initiated' do stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:logout_initiated).with(success: true) delete :logout, params: { path_year: path_year } @@ -71,6 +74,9 @@ it 'tracks the event when sp initiated' do allow(controller).to receive(:saml_request).and_return(FakeSamlLogoutRequest.new) stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:logout_initiated).with(success: true) delete :logout, params: { SAMLRequest: 'foo', path_year: path_year } @@ -84,6 +90,9 @@ it 'tracks the event when the saml request is invalid' do stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:logout_initiated).with(success: true) delete :logout, params: { SAMLRequest: 'foo', path_year: path_year } @@ -147,6 +156,9 @@ it 'tracks the request' do stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:logout_initiated).with(success: true) delete :logout, params: UriService.params( OneLogin::RubySaml::Logoutrequest.new.create(wrong_cert_settings), @@ -870,6 +882,9 @@ def name_id_version(format_urn) it 'tracks IAL2 authentication events' do stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) allow(controller).to receive(:identity_needs_verification?).and_return(false) saml_get_auth(ial2_settings) @@ -1027,6 +1042,9 @@ def name_id_version(format_urn) it 'tracks IAL2 authentication events' do stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) allow(controller).to receive(:identity_needs_verification?).and_return(false) saml_get_auth(ialmax_settings) @@ -2734,6 +2752,9 @@ def stub_requested_attributes it 'tracks the authentication without IdV redirection event' do user = create(:user, :fully_registered) stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) session[:sign_in_flow] = :sign_in allow(controller).to receive(:identity_needs_verification?).and_return(false) @@ -2784,6 +2805,9 @@ def stub_requested_attributes it 'tracks the authentication with finish_profile==true' do user = create(:user, :fully_registered) stub_analytics + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:login_completed) session[:sign_in_flow] = :sign_in allow(controller).to receive(:identity_needs_verification?).and_return(false) allow(controller).to receive(:user_has_pending_profile?).and_return(true) diff --git a/spec/controllers/sign_out_controller_spec.rb b/spec/controllers/sign_out_controller_spec.rb index c53b2c6ce42..5ed8f6b8596 100644 --- a/spec/controllers/sign_out_controller_spec.rb +++ b/spec/controllers/sign_out_controller_spec.rb @@ -22,6 +22,9 @@ it 'tracks the event' do stub_sign_in_before_2fa stub_analytics + stub_attempts_tracker + + allow(@attempts_api_tracker).to receive(:logout_initiated).with(success: true) allow(controller.decorated_sp_session).to receive(:cancel_link_url).and_return('foo') get :destroy diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index 192ee61a742..8be313f0c97 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -52,6 +52,9 @@ describe 'GET /logout' do it 'does not log user out and redirects to root' do sign_in_as_user + stub_attempts_tracker + + expect(@attempts_api_tracker).to_not receive(:logout_initiated) get :destroy expect(controller.current_user).to_not be nil expect(response).to redirect_to root_url @@ -62,6 +65,11 @@ it 'tracks a logout event' do stub_analytics sign_in_as_user + stub_attempts_tracker + + expect(@attempts_api_tracker).to receive(:logout_initiated).with( + success: true, + ) delete :destroy diff --git a/spec/features/users/sign_up_spec.rb b/spec/features/users/sign_up_spec.rb index a61e54b7f5c..a33a36d7b86 100644 --- a/spec/features/users/sign_up_spec.rb +++ b/spec/features/users/sign_up_spec.rb @@ -467,6 +467,11 @@ def clipboard_text freeze_time do analytics = FakeAnalytics.new allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(analytics) + attempts_api_tracker = AttemptsApiTrackingHelper::FakeAttemptsTracker.new + allow_any_instance_of(ApplicationController).to receive(:attempts_api_tracker).and_return( + attempts_api_tracker, + ) + expect(attempts_api_tracker).to receive(:login_completed) visit_idp_from_sp_with_ial1(:oidc) travel_to Time.zone.now + 15.seconds diff --git a/spec/support/shared_examples/sign_in.rb b/spec/support/shared_examples/sign_in.rb index 61809a45c63..9c0867a4f7f 100644 --- a/spec/support/shared_examples/sign_in.rb +++ b/spec/support/shared_examples/sign_in.rb @@ -36,6 +36,11 @@ user = create(:user, :fully_registered) analytics = FakeAnalytics.new(user:) allow_any_instance_of(ApplicationController).to receive(:analytics).and_return(analytics) + attempts_api_tracker = AttemptsApiTrackingHelper::FakeAttemptsTracker.new + allow_any_instance_of(ApplicationController).to receive(:attempts_api_tracker).and_return( + attempts_api_tracker, + ) + expect(attempts_api_tracker).to receive(:login_completed) visit_idp_from_sp_with_ial1(sp) travel_to Time.zone.now + 15.seconds