Skip to content

Commit 6a3266d

Browse files
vpomerleaudschom
authored andcommitted
fix(glean): Add missing 2FA Glean events
Because: * We want full metrics coverage for the 2FA flows before rolling out recovery phone This commit: * Add missing 2FA setup custom events, automated click events, and reason codes (ensure events have reason codes for setup from settings, inline or to replace codes) Closes #FXA-11087
1 parent 5a43625 commit 6a3266d

File tree

38 files changed

+2492
-207
lines changed

38 files changed

+2492
-207
lines changed

packages/fxa-auth-server/lib/metrics/glean/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ export function gleanMetrics(config: ConfigType) {
301301
},
302302
twoFactorAuth: {
303303
codeComplete: createEventFn('two_factor_auth_code_complete'),
304+
replaceCodeComplete: createEventFn(
305+
'two_factor_auth_replace_code_complete'
306+
),
304307
},
305308
twoStepAuthPhoneCode: {
306309
sent: createEventFn('two_step_auth_phone_code_sent'),

packages/fxa-auth-server/lib/metrics/glean/server_events.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4066,6 +4066,88 @@ class EventsServerEventLogger {
40664066
event,
40674067
});
40684068
}
4069+
/**
4070+
* Record and submit a two_factor_auth_replace_code_complete event:
4071+
* Event that indicates the user successfully replaced their two-step authentication codes
4072+
* Event is logged using internal mozlog logger.
4073+
*
4074+
* @param {string} user_agent - The user agent.
4075+
* @param {string} ip_address - The IP address. Will be used to decode Geo
4076+
* information and scrubbed at ingestion.
4077+
* @param {string} account_user_id - The firefox/mozilla account id.
4078+
* @param {string} account_user_id_sha256 - A hex string of a sha256 hash of the account's uid.
4079+
* @param {string} relying_party_oauth_client_id - The client id of the relying party.
4080+
* @param {string} relying_party_service - The service name of the relying party.
4081+
* @param {string} session_device_type - one of 'mobile', 'tablet', or ''.
4082+
* @param {string} session_entrypoint - Entrypoint to the service.
4083+
* @param {string} session_entrypoint_experiment - Identifier for the experiment the user is part of at the entrypoint.
4084+
* @param {string} session_entrypoint_variation - Identifier for the experiment variation the user is part of at the entrypoint.
4085+
* @param {string} session_flow_id - an ID generated by FxA for its flow metrics.
4086+
* @param {string} utm_campaign - A marketing campaign. For example, if a user signs into FxA from selecting a Mozilla VPN plan on Mozilla VPN's product site, then the value of this metric could be 'vpn-product-page'. The value has a max length of 128 characters with the alphanumeric characters, _ (underscore), forward slash (/), . (period), % (percentage sign), and - (hyphen) in the allowed set of characters. The special value of 'page+referral+-+not+part+of+a+campaign' is also allowed..
4087+
* @param {string} utm_content - The content on which the user acted. For example, if the user clicked on the (previously available) "Get started here" link in "Looking for Firefox Sync? Get started here", then the value for this metric would be 'fx-sync-get-started'. The value has a max length of 128 characters with the alphanumeric characters, _ (underscore), forward slash (/), . (period), % (percentage sign), and - (hyphen) in the allowed set of characters..
4088+
* @param {string} utm_medium - The "medium" on which the user acted. For example, if the user clicked on a link in an email, then the value of this metric would be 'email'. The value has a max length of 128 characters with the alphanumeric characters, _ (underscore), forward slash (/), . (period), % (percentage sign), and - (hyphen) in the allowed set of characters..
4089+
* @param {string} utm_source - The source from where the user started. For example, if the user clicked on a link on the Mozilla accounts web site, this value could be 'fx-website'. The value has a max length of 128 characters with the alphanumeric characters, _ (underscore), forward slash (/), . (period), % (percentage sign), and - (hyphen) in the allowed set of characters..
4090+
* @param {string} utm_term - This metric is similar to the `utm.source`; it is used in the Firefox browser. For example, if the user started from about:welcome, then the value could be 'aboutwelcome-default-screen'. The value has a max length of 128 characters with the alphanumeric characters, _ (underscore), forward slash (/), . (period), % (percentage sign), and - (hyphen) in the allowed set of characters..
4091+
*/
4092+
recordTwoFactorAuthReplaceCodeComplete({
4093+
user_agent,
4094+
ip_address,
4095+
account_user_id,
4096+
account_user_id_sha256,
4097+
relying_party_oauth_client_id,
4098+
relying_party_service,
4099+
session_device_type,
4100+
session_entrypoint,
4101+
session_entrypoint_experiment,
4102+
session_entrypoint_variation,
4103+
session_flow_id,
4104+
utm_campaign,
4105+
utm_content,
4106+
utm_medium,
4107+
utm_source,
4108+
utm_term,
4109+
}: {
4110+
user_agent: string;
4111+
ip_address: string;
4112+
account_user_id: string;
4113+
account_user_id_sha256: string;
4114+
relying_party_oauth_client_id: string;
4115+
relying_party_service: string;
4116+
session_device_type: string;
4117+
session_entrypoint: string;
4118+
session_entrypoint_experiment: string;
4119+
session_entrypoint_variation: string;
4120+
session_flow_id: string;
4121+
utm_campaign: string;
4122+
utm_content: string;
4123+
utm_medium: string;
4124+
utm_source: string;
4125+
utm_term: string;
4126+
}) {
4127+
const event = {
4128+
category: 'two_factor_auth',
4129+
name: 'replace_code_complete',
4130+
};
4131+
this.#record({
4132+
user_agent,
4133+
ip_address,
4134+
account_user_id,
4135+
account_user_id_sha256,
4136+
relying_party_oauth_client_id,
4137+
relying_party_service,
4138+
session_device_type,
4139+
session_entrypoint,
4140+
session_entrypoint_experiment,
4141+
session_entrypoint_variation,
4142+
session_flow_id,
4143+
utm_campaign,
4144+
utm_content,
4145+
utm_medium,
4146+
utm_source,
4147+
utm_term,
4148+
event,
4149+
});
4150+
}
40694151
/**
40704152
* Record and submit a two_step_auth_phone_code_complete event:
40714153
* User successfully entered and submitted an authentication code

packages/fxa-auth-server/lib/routes/recovery-codes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ module.exports = (log, db, config, customs, mailer, glean) => {
104104

105105
const { recoveryCodes } = request.payload;
106106
await db.updateRecoveryCodes(uid, recoveryCodes);
107+
glean.twoFactorAuth.replaceCodeComplete(request, { uid });
107108

108109
const account = await db.account(uid);
109110
const { acceptLanguage, clientAddress: geo, ua } = request.app;

packages/fxa-auth-server/lib/routes/totp.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ module.exports = (
269269
}
270270

271271
// Record that the 2fa was successfully removed
272-
glean.twoStepAuthRemove.success();
272+
glean.twoStepAuthRemove.success(request, { uid });
273273

274274
return {};
275275
},

packages/fxa-auth-server/test/local/metrics/glean.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const recordAccountDeleteCompleteStub = sinon.stub();
4242
const recordPasswordResetEmailConfirmationSentStub = sinon.stub();
4343
const recordPasswordResetEmailConfirmationSuccessStub = sinon.stub();
4444
const recordTwoFactorAuthCodeCompleteStub = sinon.stub();
45+
const recordTwoFactorAuthReplaceCodeCompleteStub = sinon.stub();
4546
const recordTwoStepAuthPhoneCodeSentStub = sinon.stub();
4647
const recordTwoStepAuthPhoneCodeSendErrorStub = sinon.stub();
4748
const recordTwoStepAuthPhoneCodeCompleteStub = sinon.stub();
@@ -107,6 +108,8 @@ const gleanProxy = proxyquire('../../../lib/metrics/glean', {
107108
recordPasswordResetEmailConfirmationSuccess:
108109
recordPasswordResetEmailConfirmationSuccessStub,
109110
recordTwoFactorAuthCodeComplete: recordTwoFactorAuthCodeCompleteStub,
111+
recordTwoFactorAuthReplaceCodeComplete:
112+
recordTwoFactorAuthReplaceCodeCompleteStub,
110113
recordTwoStepAuthPhoneCodeSent: recordTwoStepAuthPhoneCodeSentStub,
111114
recordTwoStepAuthPhoneCodeSendError:
112115
recordTwoStepAuthPhoneCodeSendErrorStub,
@@ -476,6 +479,14 @@ describe('Glean server side events', () => {
476479
assert.equal(metrics['event_name'], 'account_delete_complete');
477480
sinon.assert.calledOnce(recordAccountDeleteCompleteStub);
478481
});
482+
});
483+
484+
describe('two factor auth', () => {
485+
let glean: GleanMetricsType;
486+
487+
beforeEach(() => {
488+
glean = gleanMetrics(config);
489+
});
479490

480491
it('logs a "two_factor_auth_code_complete" event', async () => {
481492
await glean.twoFactorAuth.codeComplete(request);
@@ -484,6 +495,17 @@ describe('Glean server side events', () => {
484495
assert.equal(metrics['event_name'], 'two_factor_auth_code_complete');
485496
sinon.assert.calledOnce(recordTwoFactorAuthCodeCompleteStub);
486497
});
498+
499+
it('logs a "two_factor_auth_replace_code_complete" event', async () => {
500+
await glean.twoFactorAuth.replaceCodeComplete(request);
501+
sinon.assert.calledOnce(recordStub);
502+
const metrics = recordStub.args[0][0];
503+
assert.equal(
504+
metrics['event_name'],
505+
'two_factor_auth_replace_code_complete'
506+
);
507+
sinon.assert.calledOnce(recordTwoFactorAuthReplaceCodeCompleteStub);
508+
});
487509
});
488510

489511
describe('registration', () => {

packages/fxa-auth-server/test/local/routes/totp.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ describe('totp', () => {
135135
1,
136136
'called delete TOTP token'
137137
);
138-
139138
assert.equal(
140139
profile.deleteCache.callCount,
141140
1,

packages/fxa-content-server/app/scripts/lib/glean/accountBanner.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,20 @@ export const createRecoveryKeyView = new EventMetricType(
2121
},
2222
[]
2323
);
24+
25+
/**
26+
* User sees the reactivation banner after receiving the inactive
27+
* account email and clicking the provided link inside it.
28+
*
29+
* Generated from `account_banner.reactivation_success_view`.
30+
*/
31+
export const reactivationSuccessView = new EventMetricType(
32+
{
33+
category: 'account_banner',
34+
name: 'reactivation_success_view',
35+
sendInPings: ['events'],
36+
lifetime: 'ping',
37+
disabled: false,
38+
},
39+
[]
40+
);

packages/fxa-content-server/app/scripts/lib/glean/accountPref.js

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,59 @@ export const twoStepAuthCodesView = new EventMetricType(
363363
lifetime: 'ping',
364364
disabled: false,
365365
},
366+
['reason']
367+
);
368+
369+
/**
370+
* User viewed the modal to disable two-step authentication on their account.
371+
*
372+
* Generated from `account_pref.two_step_auth_disable_modal_view`.
373+
*/
374+
export const twoStepAuthDisableModalView = new EventMetricType(
375+
{
376+
category: 'account_pref',
377+
name: 'two_step_auth_disable_modal_view',
378+
sendInPings: ['events'],
379+
lifetime: 'ping',
380+
disabled: false,
381+
},
382+
[]
383+
);
384+
385+
/**
386+
* User viewed the banner to confirm that two-step authentication has been
387+
* disabled on their account.
388+
*
389+
* Generated from `account_pref.two_step_auth_disable_success_view`.
390+
*/
391+
export const twoStepAuthDisableSuccessView = new EventMetricType(
392+
{
393+
category: 'account_pref',
394+
name: 'two_step_auth_disable_success_view',
395+
sendInPings: ['events'],
396+
lifetime: 'ping',
397+
disabled: false,
398+
},
366399
[]
367400
);
368401

402+
/**
403+
* User viewed the banner to confirm that their authentication code was
404+
* successfully entered, either during setup or to get new codes.
405+
*
406+
* Generated from `account_pref.two_step_auth_enter_code_success_view`.
407+
*/
408+
export const twoStepAuthEnterCodeSuccessView = new EventMetricType(
409+
{
410+
category: 'account_pref',
411+
name: 'two_step_auth_enter_code_success_view',
412+
sendInPings: ['events'],
413+
lifetime: 'ping',
414+
disabled: false,
415+
},
416+
['reason']
417+
);
418+
369419
/**
370420
* User viewed form to enter one of their authentication codes (step 3).
371421
*
@@ -379,6 +429,57 @@ export const twoStepAuthEnterCodeView = new EventMetricType(
379429
lifetime: 'ping',
380430
disabled: false,
381431
},
432+
['reason']
433+
);
434+
435+
/**
436+
* User started the 2FA setup process by viewing step 1 of the funnel with manual
437+
* key entry in their authenticator app.
438+
*
439+
* Generated from `account_pref.two_step_auth_manual_code_view`.
440+
*/
441+
export const twoStepAuthManualCodeView = new EventMetricType(
442+
{
443+
category: 'account_pref',
444+
name: 'two_step_auth_manual_code_view',
445+
sendInPings: ['events'],
446+
lifetime: 'ping',
447+
disabled: false,
448+
},
449+
['reason']
450+
);
451+
452+
/**
453+
* User viewed the banner to confirm the removal of their phone number from the
454+
* account.
455+
*
456+
* Generated from `account_pref.two_step_auth_phone_remove_success_view`.
457+
*/
458+
export const twoStepAuthPhoneRemoveSuccessView = new EventMetricType(
459+
{
460+
category: 'account_pref',
461+
name: 'two_step_auth_phone_remove_success_view',
462+
sendInPings: ['events'],
463+
lifetime: 'ping',
464+
disabled: false,
465+
},
466+
[]
467+
);
468+
469+
/**
470+
* User viewed the phone verification page (step 2) of the flow to add a recovery
471+
* phone from settings.
472+
*
473+
* Generated from `account_pref.two_step_auth_phone_verify_view`.
474+
*/
475+
export const twoStepAuthPhoneVerifyView = new EventMetricType(
476+
{
477+
category: 'account_pref',
478+
name: 'two_step_auth_phone_verify_view',
479+
sendInPings: ['events'],
480+
lifetime: 'ping',
481+
disabled: false,
482+
},
382483
[]
383484
);
384485

@@ -412,7 +513,7 @@ export const twoStepAuthQrView = new EventMetricType(
412513
lifetime: 'ping',
413514
disabled: false,
414515
},
415-
[]
516+
['reason']
416517
);
417518

418519
/**

packages/fxa-content-server/app/scripts/lib/glean/cad.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import EventMetricType from '@mozilla/glean/private/metrics/event';
88

99
/**
10-
* User clicks "Start browsing" on "Connect another device page"
10+
* User clicks "Manage your account" on "Connect another device page". This CTA
11+
* used to be "Start browsing".
1112
*
1213
* Generated from `cad.startbrowsing_submit`.
1314
*/
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
// AUTOGENERATED BY glean_parser v14.5.2. DO NOT EDIT. DO NOT COMMIT.
6+
7+
import EventMetricType from '@mozilla/glean/private/metrics/event';
8+
9+
/**
10+
* User viewed the "General Application" error page
11+
*
12+
* Generated from `error.view`.
13+
*/
14+
export const view = new EventMetricType(
15+
{
16+
category: 'error',
17+
name: 'view',
18+
sendInPings: ['events'],
19+
lifetime: 'ping',
20+
disabled: false,
21+
},
22+
['reason']
23+
);

0 commit comments

Comments
 (0)