recovery code(s) > backup authentication code(s)

Update text on 2fa enter code to confirm screen
This commit is contained in:
dschom 2022-09-06 11:28:44 -07:00
Родитель f28e95071b
Коммит 687fedd36f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: F26AEE99174EE68B
59 изменённых файлов: 225 добавлений и 202 удалений

Просмотреть файл

@ -8,12 +8,12 @@
Firefox Account originally implemented multi-factor authentication (MFA) support in Q1 of 2018.
This feature used TOTP based codes and was based on RFC 6238.
Additionally, if users lost their MFA device, they could use one time recovery codes to regain access to the account.
Additionally, if users lost their MFA device, they could use one time backup authentication codes to regain access to the account.
Having MFA support has helped secure our users' accounts and given them more security flexibility.
However, over time it has become more obvious that users that lose their MFA device (usually phone) are at risk of getting locked out of their account because they don't save their recovery codes.
There are a non-trivial amount of users that dont save or download their recovery codes, which is currently the only way they can regain access.
In 2019 Q4, FxA started requiring that users confirm recovery codes before enabling MFA.
However, over time it has become more obvious that users that lose their MFA device (usually phone) are at risk of getting locked out of their account because they don't save their backup authentication codes.
There are a non-trivial amount of users that dont save or download their backup authentication codes, which is currently the only way they can regain access.
In 2019 Q4, FxA started requiring that users confirm backup authentication codes before enabling MFA.
While this did help reduce lockouts, we still want to reduce it further.
We believe that adding a new MFA method to Firefox Accounts that has the similar security properties as TOTP would allow users to have another method to recover their account.
@ -21,7 +21,7 @@ We believe that adding a new MFA method to Firefox Accounts that has the similar
## Decision Drivers
- Improve user's account security
- Reduce the risk of account lockout because of lost device/recovery code
- Reduce the risk of account lockout because of lost device/backup authentication code
- Could be completed in roughly a quarter
## Considered Options
@ -54,7 +54,7 @@ There is less security risk to users if this feature is added.
### Option B - Implement SMS as opt-in backup method
- Description
- Add the ability to have users send a recovery code to a phone number associated to the account, which could then be used to recover the account.
- Add the ability to have users send a backup authentication code to a phone number associated to the account, which could then be used to recover the account.
- Pros
- Common account recovery pattern used on many sites
- Less UX unknowns

Просмотреть файл

@ -54,7 +54,7 @@ test.describe('severity-1 #smoke', () => {
});
// https://testrail.stage.mozaws.net/index.php?/cases/view/1293450
test('can change recovery codes #1293450', async ({
test('can change backup authentication codes #1293450', async ({
credentials,
page,
pages: { settings, totp, login },
@ -78,7 +78,7 @@ test.describe('severity-1 #smoke', () => {
});
// https://testrail.stage.mozaws.net/index.php?/cases/view/1293460
test('can get new recovery codes via email #1293460', async ({
test('can get new backup authentication codes via email #1293460', async ({
target,
credentials,
pages: { page, login, settings, totp },

Просмотреть файл

@ -1675,18 +1675,18 @@ const conf = convict({
},
recoveryCodes: {
length: {
doc: 'The length of a recovery code',
doc: 'The length of a backup authentication code',
default: 10,
format: 'nat',
env: 'RECOVERY_CODE_LENGTH',
},
count: {
doc: 'Number of recovery codes to create',
doc: 'Number of backup authentication codes to create',
default: 8,
env: 'RECOVERY_CODE_COUNT',
},
notifyLowCount: {
doc: 'Notify the user when there are less than these many recovery codes',
doc: 'Notify the user when there are less than these many backup authentication codes',
default: 2,
env: 'RECOVERY_CODE_NOTIFY_LOW_COUNT',
},

Просмотреть файл

@ -37,9 +37,9 @@ as follows:
- User completes an email confirmation loop to confirm password reset.
- This produces an `accountResetToken`, a credential for subsequent requests.
- User retrieves recovery code from print out or downloaded file,
- User retrieves backup authentication code from print out or downloaded file,
and provides it to FxA web-content in the password reset flow.
- FxA web-content uses the recovery code to derive the fingerprint
- FxA web-content uses the backup authentication code to derive the fingerprint
and encryption key (recover-kid and recover-enc as defined above).
- FxA web-content requests recover-data from FxA server, providing recover-kid.
- `GET /recoveryKey/:recoveryKeyId`, authenticated with `accountResetToken`.

Просмотреть файл

@ -65,7 +65,7 @@ export const AUTH_SERVER_API_DESCRIPTION = {
| 400 | 102 | Unknown account |
| 400 | 103 | Incorrect password |
| 400 | 104 | Unverified account |
| 400 | 105 | Invalid verification code |
| 400 | 105 | Invalid authentication code |
| 400 | 106 | Invalid JSON in request body |
| 400 | 107 | Invalid parameter in request body |
| 400 | 108 | Missing parameter in request body |
@ -107,16 +107,16 @@ export const AUTH_SERVER_API_DESCRIPTION = {
| 400 | 150 | Can not resend email code to an email that does not belong to this account |
| 500 | 151 | Failed to send email |
| 422 | 151 | Failed to send email |
| 400 | 152 | Invalid token verification code |
| 400 | 153 | Expired token verification code |
| 400 | 152 | Invalid token authentication code |
| 400 | 153 | Expired token authentication code |
| 400 | 154 | TOTP token already exists for this account. |
| 400 | 155 | TOTP token not found. |
| 400 | 156 | Recovery code not found. |
| 400 | 156 | Backup authentication code not found. |
| 400 | 157 | Unavailable device command. |
| 400 | 158 | Account recovery key not found. |
| 400 | 159 | Account recovery key is not valid. |
| 400 | 158 | Account recovery key not found. |
| 400 | 159 | Account recovery key is not valid. |
| 400 | 160 | This request requires two step authentication enabled on your account. |
| 400 | 161 | Account recovery key already exists. |
| 400 | 161 | Account recovery key already exists. |
| 400 | 162 | Unknown client_id |
| 400 | 164 | Stale auth timestamp |
| 409 | 165 | Redis WATCH detected a conflicting update |

Просмотреть файл

@ -16,7 +16,7 @@ const RECOVERYCODES_GET = {
dedent`
🔒 Authenticated with session token
Return new recovery codes while removing old ones.
Return new backup authentication codes while removing old ones.
`,
],
};
@ -33,7 +33,7 @@ const SESSION_VERIFY_RECOVERYCODE_POST = {
dedent`
🔒 Authenticated with session token
Verify a session using a recovery code.
Verify a session using a backup authentication code.
`,
],
};

Просмотреть файл

@ -11,7 +11,7 @@ const TAGS = {
OAUTH: ['api', 'Oauth'],
OAUTH_SERVER: ['api', 'OAuth Server API Overview'],
PASSWORD: ['api', 'Password'],
RECOVERY_CODES: ['api', 'Recovery codes'],
RECOVERY_CODES: ['api', 'Backup authentication codes'],
RECOVERY_KEY: ['api', 'Account recovery key'],
SECURITY_EVENTS: ['api', 'Security events'],
SESSION: ['api', 'Session'],

Просмотреть файл

@ -1047,7 +1047,7 @@ AppError.recoveryCodeNotFound = () => {
code: 400,
error: 'Bad Request',
errno: ERRNO.RECOVERY_CODE_NOT_FOUND,
message: 'Recovery code not found.',
message: 'Backup authentication code not found.',
});
};

Просмотреть файл

@ -16,7 +16,7 @@ module.exports = (log, db, config, customs, mailer) => {
const codeConfig = config.recoveryCodes;
const RECOVERY_CODE_COUNT = (codeConfig && codeConfig.count) || 8;
// Validate recovery codes
// Validate backup authentication codes
const recoveryCodesSchema = validators.recoveryCodes(
RECOVERY_CODE_COUNT,
RECOVERY_CODE_SANE_MAX_LENGTH
@ -39,8 +39,8 @@ module.exports = (log, db, config, customs, mailer) => {
const { authenticatorAssuranceLevel, uid } = request.auth.credentials;
// Since TOTP and recovery codes go hand in hand, you should only be
// able to replace recovery codes in a TOTP verified session.
// Since TOTP and backup authentication codes go hand in hand, you should only be
// able to replace backup authentication codes in a TOTP verified session.
if (!authenticatorAssuranceLevel || authenticatorAssuranceLevel <= 1) {
throw errors.unverifiedSession();
}
@ -94,8 +94,8 @@ module.exports = (log, db, config, customs, mailer) => {
const { authenticatorAssuranceLevel, uid } = request.auth.credentials;
// Since TOTP and recovery codes go hand in hand, you should only be
// able to replace recovery codes in a TOTP verified session.
// Since TOTP and backup authentication codes go hand in hand, you should only be
// able to replace backup authentication codes in a TOTP verified session.
if (!authenticatorAssuranceLevel || authenticatorAssuranceLevel <= 1) {
throw errors.unverifiedSession();
}
@ -138,7 +138,7 @@ module.exports = (log, db, config, customs, mailer) => {
validate: {
payload: isA.object({
// Validation here is done with BASE_36 superset to be backwards compatible...
// Ideally all recovery codes are Crockford Base32.
// Ideally all backup authentication codes are Crockford Base32.
code: validators.recoveryCode(
RECOVERY_CODE_SANE_MAX_LENGTH,
validators.BASE_36

Просмотреть файл

@ -1,11 +1,11 @@
# The user has a low number of valid recovery codes remaining for use
codes-reminder-title = Low recovery codes remaining
codes-reminder-description = We noticed that you are running low on recovery codes. Please consider generating new codes to avoid getting locked out of your account.
# The user has a low number of valid backup authentication codes remaining for use
codes-reminder-title = Low backup authentication codes remaining
codes-reminder-description = We noticed that you are running low on backup authentication codes. Please consider generating new codes to avoid getting locked out of your account.
codes-generate = Generate codes
codes-generate-plaintext = { codes-generate }:
lowRecoveryCodes-action = Generate codes
lowRecoveryCodes-subject =
{ $numberRemaining ->
[one] 1 recovery code remaining
*[other] { $numberRemaining } recovery codes remaining
[one] 1 backup authentication code remaining
*[other] { $numberRemaining } backup authentication codes remaining
}

Просмотреть файл

@ -6,8 +6,8 @@ import { GlobalTemplateValues } from '../../../renderer';
const getSubject = (numberRemaining: number) =>
numberRemaining === 1
? '1 recovery code remaining'
: '<%= numberRemaining %> recovery codes remaining';
? '1 backup authentication code remaining'
: '<%= numberRemaining %> backup authentication codes remaining';
export const getIncludes = (numberRemaining: number): GlobalTemplateValues => ({
subject: {

Просмотреть файл

@ -5,7 +5,7 @@
<mj-section>
<mj-column>
<mj-text css-class="text-header">
<span data-l10n-id="codes-reminder-title">Low recovery codes remaining</span>
<span data-l10n-id="codes-reminder-title">Low backup authentication codes remaining</span>
</mj-text>
</mj-column>
</mj-section>
@ -13,7 +13,7 @@
<mj-section>
<mj-column>
<mj-text css-class="text-body">
<span data-l10n-id="codes-reminder-description">We noticed that you are running low on recovery codes. Please consider generating new codes to avoid getting locked out of your account.</span>
<span data-l10n-id="codes-reminder-description">We noticed that you are running low on backup authentication codes. Please consider generating new codes to avoid getting locked out of your account.</span>
</mj-text>
</mj-column>
</mj-section>

Просмотреть файл

@ -11,7 +11,7 @@ export default {
const createStory = storyWithProps(
'lowRecoveryCodes',
'Sent when a user has 2 or less recovery codes remaining.',
'Sent when a user has 2 or less backup authentication codes remaining.',
{
link: 'http://localhost:3030/settings/two_step_authentication/replace_codes?low_recovery_codes=true',
numberRemaining: 1,
@ -22,12 +22,12 @@ export const LowRecoveryCodesOne = createStory(
{
numberRemaining: 1,
},
'User has 1 recovery code remaining'
'User has 1 backup authentication code remaining'
);
export const LowRecoveryCodesMultiple = createStory(
{
numberRemaining: 2,
},
'User has 2 recovery codes remaining'
'User has 2 backup authentication codes remaining'
);

Просмотреть файл

@ -1,6 +1,6 @@
codes-reminder-title = "Low recovery codes remaining"
codes-reminder-title = "Low backup authentication codes remaining"
codes-reminder-description = "We noticed that you are running low on recovery codes. Please consider generating new codes to avoid getting locked out of your account."
codes-reminder-description = "We noticed that you are running low on backup authentication codes. Please consider generating new codes to avoid getting locked out of your account."
codes-generate-plaintext = "Generate codes:"
<%- link %>

Просмотреть файл

@ -1,4 +1,4 @@
postConsumeRecoveryCode-subject = Recovery code used
postConsumeRecoveryCode-title = Recovery code consumed
postConsumeRecoveryCode-description = You have successfully consumed a recovery code from the following device:
postConsumeRecoveryCode-subject = Backup authentication code used
postConsumeRecoveryCode-title = Backup authentication code consumed
postConsumeRecoveryCode-description = You have successfully consumed a backup authentication code from the following device:
postConsumeRecoveryCode-action = Manage account

Просмотреть файл

@ -1,7 +1,7 @@
{
"subject": {
"id": "postConsumeRecoveryCode-subject",
"message": "Recovery code used"
"message": "Backup authentication code used"
},
"action": {
"id": "postConsumeRecoveryCode-action",

Просмотреть файл

@ -5,11 +5,11 @@
<mj-section>
<mj-column>
<mj-text css-class="text-header">
<span data-l10n-id="postConsumeRecoveryCode-title">Recovery code consumed</span>
<span data-l10n-id="postConsumeRecoveryCode-title">Backup authentication code consumed</span>
</mj-text>
<mj-text css-class="text-body">
<span data-l10n-id="postConsumeRecoveryCode-description">You have successfully consumed a recovery code from the following device:</span>
<span data-l10n-id="postConsumeRecoveryCode-description">You have successfully consumed a backup authentication code from the following device:</span>
</mj-text>
</mj-column>
</mj-section>

Просмотреть файл

@ -12,7 +12,7 @@ export default {
const createStory = storyWithProps(
'postConsumeRecoveryCode',
'Sent when user has used a recovery code',
'Sent when user has used a backup authentication code',
{
...MOCK_USER_INFO,
link: 'http://localhost:3030/settings',

Просмотреть файл

@ -1,6 +1,6 @@
postConsumeRecoveryCode-title = "Recovery code consumed"
postConsumeRecoveryCode-title = "Backup authentication code consumed"
postConsumeRecoveryCode-description = "You have successfully consumed a recovery code from the following device:"
postConsumeRecoveryCode-description = "You have successfully consumed a backup authentication code from the following device:"
<%- include('/partials/userInfo/index.txt') %>

Просмотреть файл

@ -1,4 +1,4 @@
postNewRecoveryCodes-subject = New recovery codes generated
postNewRecoveryCodes-title = New recovery codes generated
postNewRecoveryCodes-description = You have successfully generated new recovery codes from the following device:
postNewRecoveryCodes-subject = New backup authentication codes generated
postNewRecoveryCodes-title = New backup authentication codes generated
postNewRecoveryCodes-description = You have successfully generated new backup authentication codes from the following device:
postNewRecoveryCodes-action = Manage account

Просмотреть файл

@ -1,7 +1,7 @@
{
"subject": {
"id": "postNewRecoveryCodes-subject",
"message": "New recovery codes generated"
"message": "New backup authentication codes generated"
},
"action": {
"id": "postNewRecoveryCodes-action",

Просмотреть файл

@ -5,11 +5,11 @@
<mj-section>
<mj-column>
<mj-text css-class="text-header">
<span data-l10n-id="postNewRecoveryCodes-title">New recovery codes generated</span>
<span data-l10n-id="postNewRecoveryCodes-title">New backup authentication codes generated</span>
</mj-text>
<mj-text css-class="text-body">
<span data-l10n-id="postNewRecoveryCodes-description">You have successfully generated new recovery codes from the following device:</span>
<span data-l10n-id="postNewRecoveryCodes-description">You have successfully generated new backup authentication codes from the following device:</span>
</mj-text>
</mj-column>
</mj-section>

Просмотреть файл

@ -12,7 +12,7 @@ export default {
const createStory = storyWithProps(
'postNewRecoveryCodes',
'Sent when user has created new recovery codes, resetting their existing ones',
'Sent when user has created new backup authentication codes, resetting their existing ones',
{
...MOCK_USER_INFO,
link: 'http://localhost:3030/settings',

Просмотреть файл

@ -1,6 +1,6 @@
postNewRecoveryCodes-title = "New recovery codes generated"
postNewRecoveryCodes-title = "New backup authentication codes generated"
postNewRecoveryCodes-description = "You have successfully generated new recovery codes from the following device:"
postNewRecoveryCodes-description = "You have successfully generated new backup authentication codes from the following device:"
<%- include('/partials/userInfo/index.txt') %>

Просмотреть файл

@ -258,7 +258,7 @@ class LoadTest(TestCase):
raise
pft = self.client.send_reset_code(email)
assert "tries" in pft.get_status()
# Get the recovery code and redeem it.
# Get the backup authentication code and redeem it.
code = self._get_code_from_email(emailAcct, "x-recovery-code")
assert code is not None, "failed to get code"
try:

Просмотреть файл

@ -36,7 +36,7 @@ function runTest(routePath, requestOptions, method) {
return route.handler(request);
}
describe('recovery codes', () => {
describe('backup authentication codes', () => {
beforeEach(() => {
log = mocks.mockLog();
customs = mocks.mockCustoms();
@ -63,7 +63,7 @@ describe('recovery codes', () => {
});
describe('GET /recoveryCodes', () => {
it('should replace recovery codes in TOTP session', () => {
it('should replace backup authentication codes in TOTP session', () => {
requestOptions.credentials.authenticatorAssuranceLevel = 2;
return runTest('/recoveryCodes', requestOptions, 'GET').then((res) => {
assert.equal(res.recoveryCodes.length, 2, 'correct default code count');
@ -71,7 +71,11 @@ describe('recovery codes', () => {
assert.equal(db.replaceRecoveryCodes.callCount, 1);
const args = db.replaceRecoveryCodes.args[0];
assert.equal(args[0], UID, 'called with uid');
assert.equal(args[1], 8, 'called with recovery code count');
assert.equal(
args[1],
8,
'called with backup authentication code count'
);
});
});
@ -87,7 +91,7 @@ describe('recovery codes', () => {
});
describe('PUT /recoveryCodes', () => {
it('should overwrite recovery codes in TOTP session', () => {
it('should overwrite backup authentication codes in TOTP session', () => {
requestOptions.credentials.authenticatorAssuranceLevel = 2;
requestOptions.payload.recoveryCodes = ['123'];
@ -98,7 +102,11 @@ describe('recovery codes', () => {
const args = db.updateRecoveryCodes.args[0];
assert.equal(args[0], UID, 'called with uid');
assert.deepEqual(args[1], ['123'], 'called with recovery codes');
assert.deepEqual(
args[1],
['123'],
'called with backup authentication codes'
);
});
});
@ -115,7 +123,7 @@ describe('recovery codes', () => {
});
describe('/session/verify/recoveryCode', () => {
it('sends email if recovery codes are low', async () => {
it('sends email if backup authentication codes are low', async () => {
db.consumeRecoveryCode = sinon.spy((code) => {
return Promise.resolve({ remaining: 1 });
});
@ -126,7 +134,7 @@ describe('recovery codes', () => {
assert.equal(args[2].numberRemaining, 1);
});
it('should rate-limit attempts to use a recovery code via customs', () => {
it('should rate-limit attempts to use a backup authentication code via customs', () => {
requestOptions.payload.code = '1234567890';
db.consumeRecoveryCode = sinon.spy((code) => {
throw error.recoveryCodeNotFound();

Просмотреть файл

@ -1031,7 +1031,7 @@ describe('lib/routes/validators:', () => {
});
});
describe('recovery codes', () => {
describe('backup authentication codes', () => {
it('allows base32 codes', () => {
assert.notExists(
validators
@ -1048,7 +1048,7 @@ describe('lib/routes/validators:', () => {
);
});
it('detects missing recovery codes', () => {
it('detects missing backup authentication codes', () => {
assert.exists(
validators.recoveryCodes(2, 10).validate({ recoveryCodes: [] }).error
);
@ -1087,14 +1087,14 @@ describe('lib/routes/validators:', () => {
});
});
describe('recovery code', () => {
it('validates recovery codes', () => {
describe('backup authentication code', () => {
it('validates backup authentication codes', () => {
assert.notExists(
validators.recoveryCode(10).validate('0123456789').error
);
});
it('invalidates recovery code', () => {
it('invalidates backup authentication code', () => {
assert.exists(validators.recoveryCode(10).validate('012345678-').error);
});

Просмотреть файл

@ -323,7 +323,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
['lowRecoveryCodesEmail', new Map<string, Test | any>([
['subject', [
{ test: 'include', expected: '2 recovery codes remaining' }
{ test: 'include', expected: '2 backup authentication codes remaining' }
]],
['headers', new Map([
['X-Link', { test: 'equal', expected: configUrl('accountRecoveryCodesUrl', 'low-recovery-codes', 'recovery-codes', 'low_recovery_codes=true', 'email', 'uid') }],
@ -332,7 +332,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
['X-Template-Version', { test: 'equal', expected: TEMPLATE_VERSIONS.lowRecoveryCodes }],
])],
['html', [
{ test: 'include', expected: '2 recovery codes remaining' },
{ test: 'include', expected: '2 backup authentication codes remaining' },
{ test: 'include', expected: decodeUrl(configHref('accountRecoveryCodesUrl', 'low-recovery-codes', 'recovery-codes', 'low_recovery_codes=true', 'email', 'uid')) },
{ test: 'include', expected: decodeUrl(configHref('privacyUrl', 'low-recovery-codes', 'privacy')) },
{ test: 'include', expected: decodeUrl(configHref('supportUrl', 'low-recovery-codes', 'support')) },
@ -340,7 +340,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
{ test: 'notInclude', expected: 'utm_source=email' },
]],
['text', [
{ test: 'include', expected: 'Low recovery codes remaining' },
{ test: 'include', expected: 'Low backup authentication codes remaining' },
{ test: 'include', expected: `Generate codes:\n${configUrl('accountRecoveryCodesUrl', 'low-recovery-codes', 'recovery-codes', 'low_recovery_codes=true', 'email', 'uid')}` },
{ test: 'include', expected: `Mozilla Privacy Policy\n${configUrl('privacyUrl', 'low-recovery-codes', 'privacy')}` },
{ test: 'include', expected: `For more info, visit Mozilla Support: ${configUrl('supportUrl', 'low-recovery-codes', 'support')}` },
@ -349,7 +349,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
])],
['lowRecoveryCodesEmail', new Map<string, Test | any>([
['subject', [
{ test: 'include', expected: '1 recovery code remaining' }
{ test: 'include', expected: '1 backup authentication code remaining' }
]]]),
{updateTemplateValues: values => ({...values, numberRemaining: 1 })}],
@ -1133,7 +1133,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
])],
['postConsumeRecoveryCodeEmail', new Map<string, Test | any>([
['subject', { test: 'equal', expected: 'Recovery code used' }],
['subject', { test: 'equal', expected: 'Backup authentication code used' }],
['headers', new Map([
['X-Link', { test: 'equal', expected: configUrl('accountSettingsUrl', 'account-consume-recovery-code', 'manage-account', 'email', 'uid') }],
['X-SES-MESSAGE-TAGS', { test: 'equal', expected: sesMessageTagsHeaderValue('postConsumeRecoveryCode') }],
@ -1141,8 +1141,8 @@ const TESTS: [string, any, Record<string, any>?][] = [
['X-Template-Version', { test: 'equal', expected: TEMPLATE_VERSIONS.postConsumeRecoveryCode }],
])],
['html', [
{ test: 'include', expected: 'Recovery code consumed' },
{ test: 'include', expected: 'You have successfully consumed a recovery code from the following device:' },
{ test: 'include', expected: 'Backup authentication code consumed' },
{ test: 'include', expected: 'You have successfully consumed a backup authentication code from the following device:' },
{ test: 'include', expected: decodeUrl(configHref('accountSettingsUrl', 'account-consume-recovery-code', 'manage-account', 'email', 'uid')) },
{ test: 'include', expected: decodeUrl(configHref('initiatePasswordChangeUrl', 'account-consume-recovery-code', 'change-password', 'email')) },
{ test: 'include', expected: decodeUrl(configHref('privacyUrl', 'account-consume-recovery-code', 'privacy')) },
@ -1155,8 +1155,8 @@ const TESTS: [string, any, Record<string, any>?][] = [
{ test: 'notInclude', expected: 'utm_source=email' },
]],
['text', [
{ test: 'include', expected: 'Recovery code consumed' },
{ test: 'include', expected: 'You have successfully consumed a recovery code from the following device:' },
{ test: 'include', expected: 'Backup authentication code consumed' },
{ test: 'include', expected: 'You have successfully consumed a backup authentication code from the following device:' },
{ test: 'include', expected: `Manage account:\n${configUrl('accountSettingsUrl', 'account-consume-recovery-code', 'manage-account', 'email', 'uid')}` },
{ test: 'include', expected: `please change your password.\n${configUrl('initiatePasswordChangeUrl', 'account-consume-recovery-code', 'change-password', 'email')}` },
{ test: 'include', expected: `Mozilla Privacy Policy\n${configUrl('privacyUrl', 'account-consume-recovery-code', 'privacy')}` },
@ -1171,7 +1171,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
])],
['postNewRecoveryCodesEmail', new Map<string, Test | any>([
['subject', { test: 'equal', expected: 'New recovery codes generated' }],
['subject', { test: 'equal', expected: 'New backup authentication codes generated' }],
['headers', new Map([
['X-Link', { test: 'equal', expected: configUrl('accountSettingsUrl', 'account-replace-recovery-codes', 'manage-account', 'email', 'uid') }],
['X-SES-MESSAGE-TAGS', { test: 'equal', expected: sesMessageTagsHeaderValue('postNewRecoveryCodes') }],
@ -1179,8 +1179,8 @@ const TESTS: [string, any, Record<string, any>?][] = [
['X-Template-Version', { test: 'equal', expected: TEMPLATE_VERSIONS.postNewRecoveryCodes }],
])],
['html', [
{ test: 'include', expected: 'New recovery codes generated' },
{ test: 'include', expected: 'You have successfully generated new recovery codes' },
{ test: 'include', expected: 'New backup authentication codes generated' },
{ test: 'include', expected: 'You have successfully generated new backup authentication codes' },
{ test: 'include', expected: decodeUrl(configHref('accountSettingsUrl', 'account-replace-recovery-codes', 'manage-account', 'email', 'uid')) },
{ test: 'include', expected: decodeUrl(configHref('initiatePasswordChangeUrl', 'account-replace-recovery-codes', 'change-password', 'email')) },
{ test: 'include', expected: decodeUrl(configHref('privacyUrl', 'account-replace-recovery-codes', 'privacy')) },
@ -1193,8 +1193,8 @@ const TESTS: [string, any, Record<string, any>?][] = [
{ test: 'notInclude', expected: 'utm_source=email' },
]],
['text', [
{ test: 'include', expected: 'New recovery codes generated' },
{ test: 'include', expected: 'You have successfully generated new recovery codes' },
{ test: 'include', expected: 'New backup authentication codes generated' },
{ test: 'include', expected: 'You have successfully generated new backup authentication codes' },
{ test: 'include', expected: `Manage account:\n${configUrl('accountSettingsUrl', 'account-replace-recovery-codes', 'manage-account', 'email', 'uid')}` },
{ test: 'include', expected: `please change your password.\n${configUrl('initiatePasswordChangeUrl', 'account-replace-recovery-codes', 'change-password', 'email')}` },
{ test: 'include', expected: `Mozilla Privacy Policy\n${configUrl('privacyUrl', 'account-replace-recovery-codes', 'privacy')}` },

Просмотреть файл

@ -11,7 +11,7 @@ const Client = require('../client')();
const otplib = require('otplib');
const BASE_36 = require('../../lib/routes/validators').BASE_36;
describe('remote recovery codes', function () {
describe('remote backup authentication codes', function () {
let server, client, email, recoveryCodes;
const recoveryCodeCount = 9;
const password = 'pssssst';
@ -53,10 +53,10 @@ describe('remote recovery codes', function () {
assert.equal(
result.recoveryCodes.length,
recoveryCodeCount,
'recovery codes returned'
'backup authentication codes returned'
);
// Verify TOTP token so that initial recovery codes are generated
// Verify TOTP token so that initial backup authentication codes are generated
const code = otplib.authenticator.generate();
return client
.verifyTotpCode(code, { metricsContext })
@ -75,12 +75,12 @@ describe('remote recovery codes', function () {
});
});
it('should create recovery codes', () => {
it('should create backup authentication codes', () => {
assert.ok(recoveryCodes);
assert.equal(
recoveryCodes.length,
recoveryCodeCount,
'recovery codes returned'
'backup authentication codes returned'
);
recoveryCodes.forEach((code) => {
assert.equal(code.length > 1, true, 'correct length');
@ -88,19 +88,19 @@ describe('remote recovery codes', function () {
});
});
it('should replace recovery codes', () => {
it('should replace backup authentication codes', () => {
return client
.replaceRecoveryCodes()
.then((result) => {
assert.ok(
result.recoveryCodes.length,
recoveryCodeCount,
'recovery codes returned'
'backup authentication codes returned'
);
assert.notDeepEqual(
result,
recoveryCodes,
'recovery codes should not match'
'backup authentication codes should not match'
);
return server.mailbox.waitForEmail(email);
@ -113,9 +113,9 @@ describe('remote recovery codes', function () {
});
});
describe('recovery code verification', () => {
describe('backup authentication code verification', () => {
beforeEach(() => {
// Create a new unverified session to test recovery codes
// Create a new unverified session to test backup authentication codes
return Client.login(config.publicUrl, email, password)
.then((response) => {
client = response;
@ -126,7 +126,7 @@ describe('remote recovery codes', function () {
);
});
it('should fail to consume unknown recovery code', () => {
it('should fail to consume unknown backup authentication code', () => {
return client
.consumeRecoveryCode('1234abcd', { metricsContext })
.then(assert.fail, (err) => {
@ -135,7 +135,7 @@ describe('remote recovery codes', function () {
});
});
it('should consume recovery code and verify session', () => {
it('should consume backup authentication code and verify session', () => {
return client
.consumeRecoveryCode(recoveryCodes[0], { metricsContext })
.then((res) => {
@ -158,7 +158,7 @@ describe('remote recovery codes', function () {
});
});
it('should consume recovery code and can remove TOTP token', () => {
it('should consume backup authentication code and can remove TOTP token', () => {
return client
.consumeRecoveryCode(recoveryCodes[0], { metricsContext })
.then((res) => {
@ -189,9 +189,9 @@ describe('remote recovery codes', function () {
});
});
describe('should notify user when recovery codes are low', () => {
describe('should notify user when backup authentication codes are low', () => {
beforeEach(() => {
// Create a new unverified session to test recovery codes
// Create a new unverified session to test backup authentication codes
return Client.login(config.publicUrl, email, password)
.then((response) => {
client = response;
@ -202,7 +202,7 @@ describe('remote recovery codes', function () {
);
});
it('should consume recovery code and verify session', () => {
it('should consume backup authentication code and verify session', () => {
return client
.consumeRecoveryCode(recoveryCodes[0], { metricsContext })
.then((res) => {

Просмотреть файл

@ -329,7 +329,10 @@ describe('remote emails', function () {
})
.then((emailData) => {
code = emailData.headers['x-recovery-code'];
assert.ok(code, 'recovery code was sent the secondary email');
assert.ok(
code,
'backup authentication code was sent the secondary email'
);
})
.then((res) => {
return client.deleteEmail(secondEmail);
@ -344,7 +347,9 @@ describe('remote emails', function () {
})
.then(
() => {
assert.fail('password recovery code shoud not have been accepted');
assert.fail(
'password backup authentication code should not have been accepted'
);
},
(err) => {
assert.equal(

Просмотреть файл

@ -60,7 +60,7 @@ describe('remote totp', function () {
assert.equal(
result.recoveryCodes.length > 1,
true,
'recovery codes returned'
'backup authentication codes returned'
);
// Verify TOTP token

Просмотреть файл

@ -231,7 +231,7 @@ var ERRORS = {
},
RECOVERY_CODE_NOT_FOUND: {
errno: 156,
message: t('Recovery code not found'),
message: t('Backup authentication code not found'),
},
DEVICE_COMMAND_UNAVAILABLE: {
errno: 157,
@ -570,11 +570,11 @@ var ERRORS = {
},
RECOVERY_CODE_REQUIRED: {
errno: 1055,
message: t('Recovery code required'),
message: t('Backup authentication code required'),
},
INVALID_RECOVERY_CODE: {
errno: 1056,
message: t('Invalid recovery code'),
message: t('Invalid backup authentication code'),
},
PASSWORD_SAME_AS_EMAIL: {
errno: 1057,

Просмотреть файл

@ -1054,16 +1054,16 @@ FxaClientWrapper.prototype = {
verifyTotpCode: createClientDelegate('verifyTotpCode'),
/**
* Consume and verifies a session with a recovery code.
* Consume and verifies a session with a backup authentication code.
*
* @param {String} sessionToken SessionToken obtained from signIn
* @param {String} code Recovery code
* @param {String} code Backup authentication code
* @returns {Promise} resolves when complete
*/
consumeRecoveryCode: createClientDelegate('consumeRecoveryCode'),
/**
* Replace all user recovery codes.
* Replace all user backup authentication codes.
*
* @param {String} sessionToken SessionToken obtained from signIn
* @returns {Promise} resolves when complete

Просмотреть файл

@ -36,7 +36,7 @@ const unblockCodeRegExp = new RegExp(unblockCodeRegExpStr, 'i');
// TOTP codes are 6 digits
const TOTP_CODE = /^[0-9]{6}$/;
// Recovery codes can be 8-10 alpha numeric characters
// Backup authentication codes can be 8-10 alpha numeric characters
const RECOVERY_CODE = /^([a-zA-Z0-9]{8,10})$/;
const utmRegex = /^[\w\/.%-]{1,128}$/;
@ -210,7 +210,7 @@ var Validate = {
},
/**
* Check if an recovery code is valid
* Check if an backup authentication code is valid
*
* @param {String} value
* @returns {Boolean}

Просмотреть файл

@ -1505,7 +1505,7 @@ const Account = Backbone.Model.extend(
},
/**
* Consume a recovery code.
* Consume a backup authentication code.
*
* @param {String} code
@ -1524,7 +1524,7 @@ const Account = Backbone.Model.extend(
},
/**
* Replaces all current recovery codes.
* Replaces all current backup authentication codes.
*
* @returns {Promise}
*/

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Recovery code model
* Backup authentication code model
*/
import Backbone from 'backbone';

Просмотреть файл

@ -622,7 +622,7 @@ var User = Backbone.Model.extend({
* @param {Object} account - account to sign up
* @param {String} password - the user's new password
* @param {String} accountResetToken - token used to issue request
* @param {String} recoveryKeyId - recoveryKeyId that maps to recovery code
* @param {String} recoveryKeyId - recoveryKeyId that maps to backup authentication code
* @param {String} kB - original kB
* @param {Object} relier - relier being signed in to
* @param {String} emailToHashWith - hash password with this email

Просмотреть файл

@ -3,7 +3,7 @@
<header>
<h1 id="fxa-save-recovery-codes">
<!-- L10N: For languages structured like English, the second phrase can read "to continue to %(serviceName)s" -->
{{#t}}Save recovery codes{{/t}} <span class="service">{{#t}}Continue to %(serviceName)s{{/t}}</span>
{{#t}}Save backup authentication codes{{/t}} <span class="service">{{#t}}Continue to %(serviceName)s{{/t}}</span>
</h1>
</header>
<section>
@ -57,7 +57,7 @@
<header>
<h1 id="fxa-confirm-recovery-code">
<!-- L10N: For languages structured like English, the second phrase can read "to continue to %(serviceName)s" -->
{{#t}}Confirm recovery code{{/t}} <span class="service">{{#t}}Continue to %(serviceName)s{{/t}}</span>
{{#t}}Confirm backup authentication code{{/t}} <span class="service">{{#t}}Continue to %(serviceName)s{{/t}}</span>
</h1>
</header>
<section>
@ -67,10 +67,10 @@
<div id="recovery-codes">
<div class="graphic graphic-recovery-codes"></div>
<p>
{{#t}}To ensure that you will be able to regain access to your account, in the event of a lost device, please enter one of your saved recovery codes.{{/t}}
{{#t}}To ensure that you will be able to regain access to your account, in the event of a lost device, please enter one of your saved backup authentication codes.{{/t}}
</p>
<div class="input-row">
<input type="text" class="tooltip-below recovery-code" placeholder="{{#t}}Recovery code{{/t}}" required autofocus autocomplete="off"/>
<input type="text" class="tooltip-below recovery-code" placeholder="{{#t}}Backup authentication code{{/t}}" required autofocus autocomplete="off"/>
</div>
<div class="button-row">
<button type="submit" class="primary-button recovery-confirm-code">{{#t}}Confirm{{/t}}</button>

Просмотреть файл

@ -1,5 +1,5 @@
<html>
<head><title>Recovery Codes</title>
<head><title>Backup Authentication Codes</title>
</head>
<body>
{{#recoveryCodes}}

Просмотреть файл

@ -1,6 +1,6 @@
<div id="main-content" class="card">
<header>
<h1 id="fxa-recovery-code-header">{{#t}}Enter recovery code{{/t}}
<h1 id="fxa-recovery-code-header">{{#t}}Enter backup authentication code{{/t}}
<span class="service">{{#t}}Continue to %(serviceName)s{{/t}}</span>
</h1>
</header>
@ -12,12 +12,12 @@
<div class="graphic graphic-recovery-codes"></div>
<p class="verification-recovery-code-message">
{{#t}}Please enter a recovery code that was provided to you during setup.{{/t}}
{{#t}}Please enter a backup authentication code that was provided to you during two step authentication setup.{{/t}}
</p>
<form novalidate>
<div class="input-row otp-code-row">
<input type="text" class="tooltip-below recovery-code" placeholder="{{#t}}Enter 10-digit recovery code{{/t}}" required autofocus />
<input type="text" class="tooltip-below recovery-code" placeholder="{{#t}}Enter 10-digit backup authentication code{{/t}}" required autofocus />
</div>
<div class="button-row">

Просмотреть файл

@ -72,7 +72,7 @@ export default {
getFormatedRecoveryCodeFilename() {
const account = this.getSignedInAccount();
let formattedFilename =
account.get('email') + ' ' + t('Firefox Recovery Codes');
account.get('email') + ' ' + t('Firefox Backup Authentication Codes');
if (formattedFilename.length > 200) {
// 200 bytes (close to filesystem max) - 4 for '.txt' extension
formattedFilename = formattedFilename.substring(0, 196);
@ -81,7 +81,7 @@ export default {
},
setupRecoveryCodes(codes, msg) {
// Store a readable version of recovery codes so that they can
// Store a readable version of backup authentication codes so that they can
// be copied, printed and downloaded
this.recoveryCodesText = '';
if (codes) {

Просмотреть файл

@ -90,7 +90,7 @@ describe('views/sign_in_recovery_code', () => {
assert.lengthOf(view.$('#fxa-recovery-code-header'), 1);
assert.include(
view.$('.verification-recovery-code-message').text(),
'recovery code'
'backup authentication code'
);
assert.equal(
view.$('#use-backup-link').attr('href'),
@ -189,7 +189,7 @@ describe('views/sign_in_recovery_code', () => {
});
});
describe('invalid recovery code', () => {
describe('invalid backup authentication code', () => {
beforeEach(() => {
sinon
.stub(account, 'consumeRecoveryCode')

Просмотреть файл

@ -528,13 +528,13 @@ const conf = (module.exports = convict({
recovery_codes: {
count: {
default: 8,
doc: 'The default number of recovery codes to create',
doc: 'The default number of backup authentication codes to create',
env: 'RECOVERY_CODE_COUNT',
format: 'nat',
},
length: {
default: 10,
doc: 'The default length of a recovery code',
doc: 'The default length of a backup authentication code',
env: 'RECOVERY_CODE_LENGTH',
format: 'nat',
},

Просмотреть файл

@ -2654,7 +2654,7 @@ const enableTotpInline = thenify(function () {
})
.then(click(selectors.INLINE_TOTP.READY_BUTTON))
// on the recovery codes screen, get the codes and advance to the confirm screen
// on the backup authentication codes screen, get the codes and advance to the confirm screen
.then(testElementExists(selectors.INLINE_RECOVERY_CODES.HEADER))
.then(visibleByQSA(selectors.INLINE_RECOVERY_CODES.RECOVERY_CODES))
.findByCssSelector(selectors.INLINE_RECOVERY_CODES.RECOVERY_CODES)

Просмотреть файл

@ -170,7 +170,7 @@ registerSuite('TOTP', {
.then(testElementExists(selectors.ENTER_EMAIL.HEADER));
},
'can change recovery codes': function () {
'can change backup authentication codes': function () {
return this.remote
.then(confirmTotpCode(secret))
.then(

Просмотреть файл

@ -36,7 +36,7 @@ const {
visibleByQSA,
} = FunctionalHelpers;
registerSuite('recovery code', {
registerSuite('backup authentication code', {
beforeEach: function () {
email = createEmail();
const self = this;
@ -81,7 +81,7 @@ registerSuite('recovery code', {
)
.then(click(selectors.TOTP.CONFIRM_CODE_BUTTON))
// Store a recovery code
// Store a backup authentication code
.findByCssSelector(
selectors.SETTINGS.SECURITY.TFA.FIRST_RECOVERY_CODE
)
@ -114,7 +114,7 @@ registerSuite('recovery code', {
},
tests: {
'can sign-in with recovery code - sync': function () {
'can sign-in with backup authentication code - sync': function () {
return (
this.remote
.then(
@ -153,7 +153,7 @@ registerSuite('recovery code', {
);
},
'can regenerate recovery code when low': function () {
'can regenerate backup authentication code when low': function () {
return (
this.remote
.then(
@ -183,8 +183,8 @@ registerSuite('recovery code', {
.then(testIsBrowserNotified('fxaccounts:login'))
// Next attempt to use recovery code will redirect to
// page where user can generate more recovery codes
// Next attempt to use backup authentication code will redirect to
// page where user can generate more backup authentication codes
.then(clearBrowserState({ force: true }))
.then(
@ -203,7 +203,7 @@ registerSuite('recovery code', {
.then(testIsBrowserNotified('fxaccounts:login'))
.then(testElementExists(selectors.CONNECT_ANOTHER_DEVICE.HEADER))
// User gets an email notifying them to generate new recovery codes.
// User gets an email notifying them to generate new backup authentication codes.
// Clicking the link in email opens the security page
.then(getEmail(email, 3))
.then((emailData) => {

Просмотреть файл

@ -112,13 +112,14 @@ export class AccountResolver {
}
@Mutation((returns) => BasicPayload, {
description: 'Creates a new password for a user and overrides encryption keys',
description:
'Creates a new password for a user and overrides encryption keys',
})
@UseGuards(GqlAuthGuard, GqlCustomsGuard)
@CatchGatewayError
public async createPassword(
@GqlSessionToken() token: string,
@Args('input', { type: () => CreatePassword })
@GqlSessionToken() token: string,
@Args('input', { type: () => CreatePassword })
input: CreatePassword
): Promise<BasicPayload> {
await this.authAPI.createPassword(token, input.email, input.password);
@ -201,7 +202,8 @@ export class AccountResolver {
}
@Mutation((returns) => ChangeRecoveryCodesPayload, {
description: 'Return new recovery codes while removing old ones.',
description:
'Return new backup authentication codes while removing old ones.',
})
@UseGuards(GqlAuthGuard, GqlCustomsGuard)
@CatchGatewayError

Просмотреть файл

@ -133,7 +133,7 @@ No changes.
- fxa-settings: update string id on updated string" ([122f2c641](https://github.com/mozilla/fxa/commit/122f2c641))
- settings: remove extra white-space before and after "Resend verification code" text Because: ([98a678bca](https://github.com/mozilla/fxa/commit/98a678bca))
- 10b272048 PR Followup ([10b272048](https://github.com/mozilla/fxa/commit/10b272048))
- settings: Disable 2FA and Recovery Code actions when account has no password ([6c911c6e8](https://github.com/mozilla/fxa/commit/6c911c6e8))
- settings: Disable 2FA and Backup Verification Code actions when account has no password ([6c911c6e8](https://github.com/mozilla/fxa/commit/6c911c6e8))
## 1.231.4

Просмотреть файл

@ -1,9 +1,9 @@
# GetDataTrio component, part of Account Recovery Key flow
get-data-trio-title = Recovery Codes
get-data-trio-title = Backup Authentication Codes
get-data-trio-title-firefox = Firefox
get-data-trio-title-firefox-account-recovery-key = Firefox account recovery key
get-data-trio-title-firefox-backup-verification-codes = Firefox backup verification codes
get-data-trio-title-firefox-backup-verification-codes = Firefox backup authentication codes
get-data-trio-download =
.title = Download
get-data-trio-copy =

Просмотреть файл

@ -12,13 +12,13 @@ import { useAccount } from '../../models';
export type DownloadContentType =
| 'Firefox account recovery key'
| 'Firefox backup verification codes'
| 'Firefox backup authentication codes'
| 'Firefox';
const DownloadContentTypeL10nMapping: Record<DownloadContentType, string> = {
Firefox: 'get-data-trio-title-firefox',
'Firefox backup verification codes':
'get-data-trio-title-firefox-backup-verification-codes',
'Firefox backup authentication codes':
'get-data-trio-title-firefox-backup-authentication-codes',
'Firefox account recovery key':
'get-data-trio-title-firefox-account-recovery-key',
};

Просмотреть файл

@ -1,9 +1,9 @@
## Two Step Authentication - replace recovery code
## Two Step Authentication - replace backup authentication code
tfa-replace-code-error-2 = There was a problem replacing your recovery codes
tfa-replace-code-error-2 = There was a problem replacing your backup authentication codes
tfa-replace-code-success = New codes have been created. Save these one-time use
codes in a safe place — youll need them to access your account if you dont
backup authentication codes in a safe place — youll need them to access your account if you dont
have your mobile device.
tfa-replace-code-success-alert-2 = Account recovery codes updated
tfa-replace-code-success-alert-2 = Account backup authentication codes updated
tfa-replace-code-1-2 = Step 1 of 2
tfa-replace-code-2-2 = Step 2 of 2

Просмотреть файл

@ -61,11 +61,11 @@ it('renders', async () => {
expect(screen.getByTestId('databutton-download')).toHaveAttribute(
'download',
expect.stringContaining('Firefox backup verification codes')
expect.stringContaining('Firefox backup authentication codes')
);
});
it('displays an error when fails to fetch new recovery codes', async () => {
it('displays an error when fails to fetch new backup authentication codes', async () => {
const account = {
replaceRecoveryCodes: jest.fn().mockRejectedValue(new Error('wat')),
} as unknown as Account;
@ -80,7 +80,7 @@ it('displays an error when fails to fetch new recovery codes', async () => {
expect(context.alertBarInfo?.error).toBeCalledTimes(1);
});
it('forces users to validate recovery code', async () => {
it('forces users to validate backup authentication code', async () => {
await renderPage2faReplaceRecoveryCodes();
fireEvent.click(screen.getByTestId('ack-recovery-code'));
@ -89,7 +89,7 @@ it('forces users to validate recovery code', async () => {
);
});
it('will not allow bad recovery code', async () => {
it('will not allow bad backup authentication code', async () => {
await renderPage2faReplaceRecoveryCodes();
fireEvent.click(screen.getByTestId('ack-recovery-code'));
await typeByTestIdFn('recovery-code-input-field')('xyz');

Просмотреть файл

@ -38,7 +38,7 @@ export const Page2faReplaceRecoveryCodes = (_: RouteComponentProps) => {
l10n.getString(
'tfa-replace-code-success-alert-2',
null,
'Account recovery codes updated'
'Account backup authentication codes updated'
)
);
navigate(HomePath + '#two-step-authentication', { replace: true });
@ -48,7 +48,8 @@ export const Page2faReplaceRecoveryCodes = (_: RouteComponentProps) => {
try {
const { success } = await account.updateRecoveryCodes(recoveryCodes);
if (!success) throw new Error('Update recovery codes not successful.');
if (!success)
throw new Error('Update backup authentication codes not successful.');
alertSuccessAndGoHome();
} catch (e) {
@ -56,7 +57,7 @@ export const Page2faReplaceRecoveryCodes = (_: RouteComponentProps) => {
l10n.getString(
'tfa-replace-code-error-2',
null,
'There was a problem replacing your recovery codes'
'There was a problem replacing your backup authentication codes'
)
);
}
@ -82,7 +83,7 @@ export const Page2faReplaceRecoveryCodes = (_: RouteComponentProps) => {
l10n.getString(
'tfa-replace-code-error-2',
null,
'There was a problem creating your recovery codes'
'There was a problem creating your backup authentication codes'
)
);
}
@ -131,9 +132,9 @@ export const Page2faReplaceRecoveryCodes = (_: RouteComponentProps) => {
<>
<div className="my-2" data-testid="2fa-recovery-codes">
<Localized id="tfa-replace-code-success">
New codes have been created. Save these one-time use codes in a
safe place youll need them to access your account if you dont
have your mobile device.
New codes have been created. Save these one-time use backup
authentication codes in a safe place youll need them to access
your account if you dont have your mobile device.
</Localized>
<div className="mt-6 flex flex-col items-center h-auto justify-between">
{recoveryCodes.length > 0 ? (
@ -141,7 +142,7 @@ export const Page2faReplaceRecoveryCodes = (_: RouteComponentProps) => {
value={recoveryCodes}
separator=" "
onCopy={copyRecoveryCodes}
contentType="Firefox backup verification codes"
contentType="Firefox backup authentication codes"
/>
) : (
<LoadingSpinner />
@ -214,7 +215,7 @@ const RecoveryCodeCheck = ({
l10n.getString(
'tfa-incorrect-recovery-code',
null,
'Incorrect recovery code'
'Incorrect backup authentication code'
)
);
return;
@ -230,16 +231,16 @@ const RecoveryCodeCheck = ({
<form onSubmit={recoveryCodeForm.handleSubmit(onSubmit)}>
<Localized id="tfa-enter-code-to-confirm">
<p className="mt-4 mb-4">
Please enter one of your recovery codes now to confirm you've saved
it. You'll need a code if you lose your device and want to access your
account.
Please enter one of your backup authentication codes now to confirm
you've saved it. Youll need a code to login if you dont have access
to your mobile device.
</p>
</Localized>
<div className="mt-4 mb-6" data-testid="recovery-code-input">
<Localized id="tfa-enter-recovery-code" attrs={{ label: true }}>
<InputText
name="recoveryCode"
label="Enter a recovery code"
label="Enter a backup authentication code"
prefixDataTestId="recovery-code"
autoFocus
onChange={() => {

Просмотреть файл

@ -12,8 +12,8 @@ tfa-button-finish = Finish
tfa-incorrect-totp = Incorrect two-step authentication code
tfa-cannot-retrieve-code = There was a problem retrieving your code.
tfa-cannot-verify-code-3 = There was a problem confirming your recovery code
tfa-incorrect-recovery-code = Incorrect recovery code
tfa-cannot-verify-code-3 = There was a problem confirming your backup authentication code
tfa-incorrect-recovery-code = Incorrect backup authentication code
tfa-enabled = Two-step authentication enabled
tfa-scan-this-code = Scan this QR code using one of <linkExternal>these
@ -34,13 +34,20 @@ tfa-enter-secret-key = Enter this secret key into your authenticator app:
tfa-enter-totp = Now enter the security code from the authentication app.
tfa-input-enter-totp =
.label = Enter security code
tfa-save-these-codes = Save these one-time use codes in a safe place for when
tfa-save-these-codes = Save these one-time use backup authentication codes in a safe place for when
you dont have your mobile device.
tfa-enter-code-to-confirm = Please enter one of your recovery codes now to
<<<<<<< HEAD
tfa-enter-code-to-confirm = Please enter one of your backup authentication codes now to
confirm youve saved it. Youll need a code if you lose your device and want
to access your account.
tfa-enter-recovery-code =
.label = Enter a recovery code
=======
tfa-enter-code-to-confirm-1 = Please enter one of your backup authentication codes now to
confirm youve saved it. Youll need a code to login if you dont have access to your
mobile device.
tfa-enter-recovery-code-1 =
>>>>>>> 782bda91e4 (Update text on 2fa enter code to confirm screen)
.label = Enter a backup authentication code
##

Просмотреть файл

@ -135,7 +135,7 @@ describe('step 2', () => {
resetCheckcodeMock();
});
it('shows the recovery codes when valid auth code is submitted', async () => {
it('shows the backup authentication codes when valid auth code is submitted', async () => {
await act(async () => {
render();
});
@ -150,7 +150,7 @@ describe('step 2', () => {
);
expect(screen.getByTestId('databutton-download')).toHaveAttribute(
'download',
expect.stringContaining('Firefox backup verification codes')
expect.stringContaining('Firefox backup authentication codes')
);
});
@ -182,12 +182,12 @@ describe('step 3', () => {
resetCheckcodeMock();
});
it('renders the recovery code form', async () => {
it('renders the backup authentication code form', async () => {
await getRecoveryCodes();
expect(screen.getByTestId('recovery-code-input-field')).toBeInTheDocument();
});
it('shows an error when an incorrect recovery code is entered', async () => {
it('shows an error when an incorrect backup authentication code is entered', async () => {
await getRecoveryCodes();
await act(async () => {
await fireEvent.input(screen.getByTestId('recovery-code-input-field'), {
@ -199,7 +199,7 @@ describe('step 3', () => {
});
expect(screen.getByTestId('tooltip')).toBeInTheDocument();
expect(screen.getByTestId('tooltip')).toHaveTextContent(
'Incorrect recovery code'
'Incorrect backup authentication code'
);
});

Просмотреть файл

@ -105,7 +105,7 @@ export const PageTwoStepAuthentication = (_: RouteComponentProps) => {
l10n.getString(
'tfa-cannot-verify-code-3',
null,
'There was a problem confirming your recovery code'
'There was a problem confirming your backup authentication code'
)
);
}
@ -149,7 +149,7 @@ export const PageTwoStepAuthentication = (_: RouteComponentProps) => {
l10n.getString(
'tfa-incorrect-recovery-code',
null,
'Incorrect recovery code'
'Incorrect backup authentication code'
)
);
return;
@ -310,8 +310,8 @@ export const PageTwoStepAuthentication = (_: RouteComponentProps) => {
<>
<div className="my-2" data-testid="2fa-recovery-codes">
<Localized id="tfa-save-these-codes">
Save these one-time use codes in a safe place for when you dont
have your mobile device.
Save these one-time use backup authentication codes in a safe
place for when you dont have your mobile device.
</Localized>
<div className="mt-6 flex flex-col items-center justify-between">
<DataBlock
@ -319,7 +319,7 @@ export const PageTwoStepAuthentication = (_: RouteComponentProps) => {
separator=" "
onAction={logDataTrioActionEvent}
onCopy={copyRecoveryCodes}
contentType="Firefox backup verification codes"
contentType="Firefox backup authentication codes"
></DataBlock>
</div>
</div>
@ -350,16 +350,16 @@ export const PageTwoStepAuthentication = (_: RouteComponentProps) => {
<form onSubmit={recoveryCodeForm.handleSubmit(onRecoveryCodeSubmit)}>
<Localized id="tfa-enter-code-to-confirm">
<p className="mt-4 mb-4">
Please enter one of your recovery codes now to confirm you've
saved it. You'll need a code if you lose your device and want to
access your account.
Please enter one of your backup authentication codes now to
confirm you've saved it. Youll need a code to login if you dont
have access to your mobile device.
</p>
</Localized>
<div className="mt-4 mb-6" data-testid="recovery-code-input">
<Localized id="tfa-enter-recovery-code" attrs={{ label: true }}>
<InputText
name="recoveryCode"
label="Enter a recovery code"
label="Enter a backup authentication code"
prefixDataTestId="recovery-code"
autoFocus
onChange={() => {

Просмотреть файл

@ -18,9 +18,9 @@ tfa-row-cannot-verify-session-4 = Sorry, there was a problem confirming your ses
tfa-row-disable-modal-heading = Disable two-step authentication?
tfa-row-disable-modal-confirm = Disable
tfa-row-disable-modal-explain = You wont be able to undo this action. You also
have the option of <linkExternal>replacing your recovery codes</linkExternal>.
have the option of <linkExternal>replacing your backup authentication codes</linkExternal>.
tfa-row-cannot-disable-2 = Two-step authentication could not be disabled
tfa-row-change-modal-heading = Change recovery codes?
tfa-row-change-modal-heading = Change backup authentication codes?
tfa-row-change-modal-confirm = Change
tfa-row-change-modal-explain = You wont be able to undo this action.

Просмотреть файл

@ -145,8 +145,8 @@ export const UnitRowTwoStepAuth = () => {
Disable two-step authentication?
</h2>
</Localized>
{/* "replacing recovery codes" link below will actually drop you into
recovery codes flow in the future. */}
{/* "replacing backup authentication codes" link below will actually drop you into
backup authentication codes flow in the future. */}
<Localized
id="tfa-row-disable-modal-explain"
elems={{
@ -167,7 +167,7 @@ export const UnitRowTwoStepAuth = () => {
className="link-blue"
href="https://support.mozilla.org/en-US/kb/reset-your-firefox-account-password-recovery-keys"
>
replacing your recovery codes
replacing your backup authentication codes
</LinkExternal>
.
</p>
@ -207,7 +207,7 @@ export const UnitRowTwoStepAuth = () => {
className="font-bold text-xl text-center mb-2"
data-testid="change-codes-modal-header"
>
Change recovery codes?
Change backup authentication codes?
</h2>
</Localized>
<Localized id="tfa-row-change-modal-explain">

Просмотреть файл

@ -227,7 +227,7 @@ const ERRORS = {
},
RECOVERY_CODE_NOT_FOUND: {
errno: 156,
message: 'Recovery code not found',
message: 'Backup authentication code not found',
},
DEVICE_COMMAND_UNAVAILABLE: {
errno: 157,
@ -568,11 +568,11 @@ const ERRORS = {
},
RECOVERY_CODE_REQUIRED: {
errno: 1055,
message: 'Recovery code required',
message: 'Backup authentication code required',
},
INVALID_RECOVERY_CODE: {
errno: 1056,
message: 'Invalid recovery code',
message: 'Invalid backup authentication code',
},
PASSWORD_SAME_AS_EMAIL: {
errno: 1057,