Skip to content

Commit 02197b8

Browse files
committed
feat(passkey): Add new postRemovePasskey email template
Because: * We want to sent a security notification to users when a passkey is deleted This commit: * Adds the new template with MJML and TXT versions, l10n * Wires up the passkey deletion route to send out the email when deletion is successful Closes #FXA-13368
1 parent 62e0d5b commit 02197b8

14 files changed

Lines changed: 656 additions & 0 deletions

File tree

libs/accounts/email-renderer/gruntfile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
'src/templates/postConsumeRecoveryCode/en.ftl',
9393
'src/templates/postNewRecoveryCodes/en.ftl',
9494
'src/templates/postRemoveAccountRecovery/en.ftl',
95+
'src/templates/postRemovePasskey/en.ftl',
9596
'src/templates/postRemoveRecoveryPhone/en.ftl',
9697
'src/templates/postRemoveSecondary/en.ftl',
9798
'src/templates/postRemoveTwoStepAuthentication/en.ftl',

libs/accounts/email-renderer/src/renderer/__snapshots__/fxa-email-renderer.spec.ts.snap

Lines changed: 402 additions & 0 deletions
Large diffs are not rendered by default.

libs/accounts/email-renderer/src/renderer/email-link-builder.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const TEMPLATE_NAME_TO_CAMPAIGN_MAP: Record<string, string> = {
2626
postVerifySecondary: 'account-email-verified',
2727
postAddLinkedAccount: 'account-linked',
2828
postAddPasskey: 'passkey-registered',
29+
postRemovePasskey: 'passkey-removed',
2930
postAddAccountRecoveryConfirm: 'confirm-account-recovery',
3031
verifyEmail: 'welcome',
3132
verifyLoginCode: 'new-device-signin',
@@ -64,6 +65,7 @@ const TEMPLATE_NAME_TO_CONTENT_MAP: Record<string, string> = {
6465
postVerifySecondary: 'account-email-verified',
6566
postAddLinkedAccount: 'account-linked',
6667
postAddPasskey: 'passkey-registered',
68+
postRemovePasskey: 'passkey-removed',
6769
postAddAccountRecoveryConfirm: 'confirm-account-recovery',
6870
verifyEmail: 'welcome',
6971
verifyLoginCode: 'new-device-signin',

libs/accounts/email-renderer/src/renderer/fxa-email-renderer.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,20 @@ describe('FxA Email Renderer', () => {
356356
expect(email.html).toMatchSnapshot('matches full email snapshot');
357357
});
358358

359+
it('should render renderPostRemovePasskey', async () => {
360+
const email = await renderer.renderPostRemovePasskey({
361+
date: 'Jan 1, 2024',
362+
device: mockDevice,
363+
location: mockLocation,
364+
link: mockLink,
365+
passwordChangeLink: mockLinkPasswordChange,
366+
time: '12:00 PM',
367+
...defaultLayoutTemplateValues,
368+
});
369+
expect(email).toBeDefined();
370+
expect(email.html).toMatchSnapshot('matches full email snapshot');
371+
});
372+
359373
it('should render renderPostAddPasskey with Sync note', async () => {
360374
const email = await renderer.renderPostAddPasskey({
361375
date: 'Jan 1, 2024',

libs/accounts/email-renderer/src/renderer/fxa-email-renderer.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import * as PostChangeTwoStepAuthentication from '../templates/postChangeTwoStep
3434
import * as PostConsumeRecoveryCode from '../templates/postConsumeRecoveryCode';
3535
import * as PostNewRecoveryCodes from '../templates/postNewRecoveryCodes';
3636
import * as PostRemoveAccountRecovery from '../templates/postRemoveAccountRecovery';
37+
import * as PostRemovePasskey from '../templates/postRemovePasskey';
3738
import * as PostRemoveRecoveryPhone from '../templates/postRemoveRecoveryPhone';
3839
import * as PostRemoveSecondary from '../templates/postRemoveSecondary';
3940
import * as PostRemoveTwoStepAuthentication from '../templates/postRemoveTwoStepAuthentication';
@@ -308,6 +309,18 @@ export class FxaEmailRenderer extends EmailRenderer {
308309
});
309310
}
310311

312+
async renderPostRemovePasskey(
313+
opts: WithFxaLayouts<PostRemovePasskey.TemplateData>
314+
) {
315+
return this.renderEmail({
316+
template: PostRemovePasskey.template,
317+
version: PostRemovePasskey.version,
318+
layout: PostRemovePasskey.layout,
319+
includes: PostRemovePasskey.includes,
320+
...opts,
321+
});
322+
}
323+
311324
async renderPostAddRecoveryPhone(
312325
opts: WithFxaLayouts<PostAddRecoveryPhone.TemplateData>
313326
) {

libs/accounts/email-renderer/src/templates/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export * as postChangePrimary from './postChangePrimary';
2727
export * as postRemoveSecondary from './postRemoveSecondary';
2828
export * as postAddLinkedAccount from './postAddLinkedAccount';
2929
export * as postAddPasskey from './postAddPasskey';
30+
export * as postRemovePasskey from './postRemovePasskey';
3031
export * as postAddTwoStepAuthentication from './postAddTwoStepAuthentication';
3132
export * as postChangeTwoStepAuthentication from './postChangeTwoStepAuthentication';
3233
export * as postRemoveTwoStepAuthentication from './postRemoveTwoStepAuthentication';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
postRemovePasskey-subject = Passkey deleted
2+
postRemovePasskey-preview = A passkey was removed from your account
3+
postRemovePasskey-title = You deleted your passkey
4+
postRemovePasskey-description = You’ll need to use another method to sign in.
5+
postRemovePasskey-requested-from = You requested this from:
6+
postRemovePasskey-action = Manage account
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
<mj-section>
6+
<mj-column>
7+
<mj-text css-class="text-header">
8+
<span data-l10n-id="postRemovePasskey-title">You deleted your passkey</span>
9+
</mj-text>
10+
11+
<mj-text css-class="text-body">
12+
<span data-l10n-id="postRemovePasskey-description">You’ll need to use another method to sign in.</span>
13+
</mj-text>
14+
15+
<mj-text css-class="text-body-no-margin">
16+
<span data-l10n-id="postRemovePasskey-requested-from">You requested this from:</span>
17+
</mj-text>
18+
</mj-column>
19+
</mj-section>
20+
<%- include('/partials/userInfo/index.mjml') %>
21+
22+
<%- include('/partials/button/index.mjml', {
23+
buttonL10nId: "postRemovePasskey-action",
24+
buttonText: "Manage account"
25+
}) %>
26+
27+
<%- include('/partials/automatedEmailChangePassword/index.mjml') %>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
import { Meta } from '@storybook/html';
6+
import { storyWithProps } from '../../storybook-email';
7+
import { includes, TemplateData } from './index';
8+
import { MOCK_USER_INFO } from '../../partials/userInfo/mocks';
9+
10+
export default {
11+
title: 'FxA Emails/Templates/postRemovePasskey',
12+
} as Meta;
13+
14+
const data = {
15+
...MOCK_USER_INFO,
16+
link: 'http://localhost:3030/settings',
17+
passwordChangeLink: 'http://localhost:3030/settings/change_password',
18+
supportUrl: 'https://support.mozilla.org',
19+
};
20+
21+
const createStory = storyWithProps<TemplateData>(
22+
'postRemovePasskey',
23+
'Sent when a user successfully deletes a passkey.',
24+
data,
25+
includes
26+
);
27+
28+
export const PostRemovePasskey = createStory();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
import { TemplateData as AutomatedEmailChangePasswordTemplateData } from '../../partials/automatedEmailChangePassword';
6+
import { TemplateData as UserInfoTemplateData } from '../../partials/userInfo';
7+
8+
export type TemplateData = AutomatedEmailChangePasswordTemplateData &
9+
UserInfoTemplateData & {
10+
link: string;
11+
};
12+
13+
export const template = 'postRemovePasskey';
14+
export const version = 1;
15+
export const layout = 'fxa';
16+
export const includes = {
17+
subject: {
18+
id: 'postRemovePasskey-subject',
19+
message: 'Passkey deleted',
20+
},
21+
action: {
22+
id: 'postRemovePasskey-action',
23+
message: 'Manage account',
24+
},
25+
preview: {
26+
id: 'postRemovePasskey-preview',
27+
message: 'A passkey was removed from your account',
28+
},
29+
};

0 commit comments

Comments
 (0)