Skip to content

Commit cb1d193

Browse files
authored
Merge pull request #18439 from mozilla/FXA-10497-acct-delete-metrics
feat(metrics): add more metrics around account deletion
2 parents 7f087cf + bb33427 commit cb1d193

9 files changed

Lines changed: 157 additions & 8 deletions

File tree

packages/fxa-auth-server/lib/account-delete.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,31 +129,46 @@ export class AccountDeleteManager {
129129
public async deleteAccount(
130130
uid: string,
131131
reason: ReasonForDeletion,
132-
customerId?: string
132+
customerId?: string,
133+
request?: AuthRequest
133134
) {
134135
// there's a day's time between shceduling and deletion so we need to check
135136
// if the account has became active in the meantime
136137
if (
137138
reason === ReasonForDeletion.InactiveAccountScheduled &&
138139
(await this.inactiveAccountsManager.isActive(uid))
139140
) {
140-
this.glean.inactiveAccountDeletion.deletionSkipped(requestForGlean, {
141-
uid,
142-
reason: 'active_account',
143-
});
141+
this.glean.inactiveAccountDeletion.deletionSkipped(
142+
request ?? requestForGlean,
143+
{
144+
uid,
145+
reason: 'active_account',
146+
}
147+
);
144148
this.statsd.increment('account.inactive.deletion.skipped.active');
145149
return;
146150
}
147151

148152
await this.deleteAccountFromDb(uid);
149153
await this.deleteOAuthTokens(uid);
154+
155+
// data eng rely on this to delete the account data from BQ.
156+
// user self-deletes are logged when the client request was handled
157+
if (reason !== ReasonForDeletion.UserRequested) {
158+
this.log.info('accountDeleted.byCloudTask', { uid });
159+
}
160+
150161
// see comment in the function on why we are not awaiting
151162
this.deletePushboxRecords(uid);
152163

153164
await this.deleteSubscriptions(uid, reason, customerId);
154165
await this.deleteFirestoreCustomer(uid);
155166
await this.appleIap?.purchaseManager.deletePurchases(uid);
156167
await this.playBilling?.purchaseManager.deletePurchases(uid);
168+
this.statsd.increment('account.destroy.success', { reason });
169+
this.glean.account.deleteTaskHandled(request ?? requestForGlean, {
170+
reason,
171+
});
157172
}
158173

159174
/**
@@ -171,6 +186,7 @@ export class AccountDeleteManager {
171186
try {
172187
await this.deleteAccountFromDb(uid);
173188
await this.deleteOAuthTokens(uid);
189+
this.statsd.increment('account.destroy.quick-delete');
174190
} catch (error) {
175191
// If the account wasn't fully deleted, we should log the error and
176192
// still queue the account for cleanup.

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ export function gleanMetrics(config: ConfigType) {
298298

299299
account: {
300300
deleteComplete: createEventFn('account_delete_complete'),
301+
deleteTaskHandled: createEventFn('account_delete_task_handled', {
302+
additionalMetrics: extraKeyReasonCb,
303+
}),
301304
},
302305
twoFactorAuth: {
303306
codeComplete: createEventFn('two_factor_auth_code_complete'),

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

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,94 @@ class EventsServerEventLogger {
556556
event,
557557
});
558558
}
559+
/**
560+
* Record and submit a account_delete_task_handled event:
561+
* An account was deleted from FxA.
562+
* Event is logged using internal mozlog logger.
563+
*
564+
* @param {string} user_agent - The user agent.
565+
* @param {string} ip_address - The IP address. Will be used to decode Geo
566+
* information and scrubbed at ingestion.
567+
* @param {string} account_user_id - The firefox/mozilla account id.
568+
* @param {string} account_user_id_sha256 - A hex string of a sha256 hash of the account's uid.
569+
* @param {string} relying_party_oauth_client_id - The client id of the relying party.
570+
* @param {string} relying_party_service - The service name of the relying party.
571+
* @param {string} session_device_type - one of 'mobile', 'tablet', or ''.
572+
* @param {string} session_entrypoint - Entrypoint to the service.
573+
* @param {string} session_entrypoint_experiment - Identifier for the experiment the user is part of at the entrypoint.
574+
* @param {string} session_entrypoint_variation - Identifier for the experiment variation the user is part of at the entrypoint.
575+
* @param {string} session_flow_id - an ID generated by FxA for its flow metrics.
576+
* @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..
577+
* @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..
578+
* @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..
579+
* @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..
580+
* @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..
581+
* @param {string} reason - The reason for the deletion..
582+
*/
583+
recordAccountDeleteTaskHandled({
584+
user_agent,
585+
ip_address,
586+
account_user_id,
587+
account_user_id_sha256,
588+
relying_party_oauth_client_id,
589+
relying_party_service,
590+
session_device_type,
591+
session_entrypoint,
592+
session_entrypoint_experiment,
593+
session_entrypoint_variation,
594+
session_flow_id,
595+
utm_campaign,
596+
utm_content,
597+
utm_medium,
598+
utm_source,
599+
utm_term,
600+
reason,
601+
}: {
602+
user_agent: string;
603+
ip_address: string;
604+
account_user_id: string;
605+
account_user_id_sha256: string;
606+
relying_party_oauth_client_id: string;
607+
relying_party_service: string;
608+
session_device_type: string;
609+
session_entrypoint: string;
610+
session_entrypoint_experiment: string;
611+
session_entrypoint_variation: string;
612+
session_flow_id: string;
613+
utm_campaign: string;
614+
utm_content: string;
615+
utm_medium: string;
616+
utm_source: string;
617+
utm_term: string;
618+
reason: string;
619+
}) {
620+
const event = {
621+
category: 'account',
622+
name: 'delete_task_handled',
623+
extra: {
624+
reason: String(reason),
625+
},
626+
};
627+
this.#record({
628+
user_agent,
629+
ip_address,
630+
account_user_id,
631+
account_user_id_sha256,
632+
relying_party_oauth_client_id,
633+
relying_party_service,
634+
session_device_type,
635+
session_entrypoint,
636+
session_entrypoint_experiment,
637+
session_entrypoint_variation,
638+
session_flow_id,
639+
utm_campaign,
640+
utm_content,
641+
utm_medium,
642+
utm_source,
643+
utm_term,
644+
event,
645+
});
646+
}
559647
/**
560648
* Record and submit a account_password_reset event:
561649
* Forgot Password Complete Successful completion of the forgot password funnel.

packages/fxa-auth-server/lib/routes/account.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,9 @@ export class AccountHandler {
19061906
ReasonForDeletion.UserRequested
19071907
);
19081908

1909+
// data eng rely on this to delete the account data from BQ
1910+
this.log.info('accountDeleted.ByRequest', { uid: accountRecord.uid });
1911+
19091912
const result = await getAccountCustomerByUid(accountRecord.uid);
19101913
await this.accountTasks.deleteAccount({
19111914
uid: accountRecord.uid,

packages/fxa-auth-server/lib/routes/cloud-tasks.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ export class CloudTaskHandler {
2727
this.emailCloudTaskManager = Container.get(EmailCloudTaskManager);
2828
}
2929

30-
async deleteAccount(taskPayload: DeleteAccountTask) {
30+
async deleteAccount(taskPayload: DeleteAccountTask, request?: AuthRequest) {
3131
this.log.debug('Received delete account task', taskPayload);
3232
await this.accountDeleteManager.deleteAccount(
3333
taskPayload.uid,
3434
taskPayload.reason,
35-
taskPayload.customerId
35+
taskPayload.customerId,
36+
request
3637
);
3738
return {};
3839
}
@@ -81,7 +82,10 @@ export const cloudTaskRoutes = (
8182
},
8283
},
8384
handler: (request: AuthRequest) =>
84-
cloudTaskHandler.deleteAccount(request.payload as DeleteAccountTask),
85+
cloudTaskHandler.deleteAccount(
86+
request.payload as DeleteAccountTask,
87+
request
88+
),
8589
},
8690

8791
{

packages/fxa-auth-server/test/local/account-delete.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,11 @@ describe('AccountDeleteManager', function () {
281281
sinon.assert.calledWithMatch(mockFxaDb.deleteAccount, {
282282
uid,
283283
});
284+
sinon.assert.calledOnceWithExactly(
285+
mockLog.info,
286+
'accountDeleted.byCloudTask',
287+
{ uid }
288+
);
284289
});
285290
});
286291
});

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const recordThirdPartyAuthGoogleRegCompleteStub = sinon.stub();
3939
const recordThirdPartyAuthAppleRegCompleteStub = sinon.stub();
4040
const recordThirdPartyAuthSetPasswordCompleteStub = sinon.stub();
4141
const recordAccountDeleteCompleteStub = sinon.stub();
42+
const recordAccountDeleteTaskHandledStub = sinon.stub();
4243
const recordPasswordResetEmailConfirmationSentStub = sinon.stub();
4344
const recordPasswordResetEmailConfirmationSuccessStub = sinon.stub();
4445
const recordTwoFactorAuthCodeCompleteStub = sinon.stub();
@@ -106,6 +107,7 @@ const gleanProxy = proxyquire('../../../lib/metrics/glean', {
106107
recordThirdPartyAuthSetPasswordComplete:
107108
recordThirdPartyAuthSetPasswordCompleteStub,
108109
recordAccountDeleteComplete: recordAccountDeleteCompleteStub,
110+
recordAccountDeleteTaskHandled: recordAccountDeleteTaskHandledStub,
109111
recordPasswordResetEmailConfirmationSent:
110112
recordPasswordResetEmailConfirmationSentStub,
111113
recordPasswordResetEmailConfirmationSuccess:

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3892,6 +3892,11 @@ describe('/account/destroy', () => {
38923892
assert.calledOnceWithExactly(glean.account.deleteComplete, mockRequest, {
38933893
uid,
38943894
});
3895+
sinon.assert.calledOnceWithExactly(
3896+
mockLog.info,
3897+
'accountDeleted.ByRequest',
3898+
{ uid }
3899+
);
38953900
});
38963901
});
38973902

packages/fxa-shared/metrics/glean/fxa-backend-metrics.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,29 @@ account:
114114
expires: never
115115
data_sensitivity:
116116
- interaction
117+
delete_task_handled:
118+
type: event
119+
description: |
120+
An account was deleted from FxA.
121+
lifetime: ping
122+
send_in_pings:
123+
- events
124+
notification_emails:
125+
126+
127+
bugs:
128+
- https://mozilla-hub.atlassian.net/browse/FXA-10497
129+
data_reviews:
130+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1830504
131+
expires: never
132+
data_sensitivity:
133+
- technical
134+
extra_keys:
135+
reason:
136+
description: |
137+
The reason for the deletion.
138+
type: string
139+
117140

118141
relying_party:
119142
service:

0 commit comments

Comments
 (0)