diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 0711989218..c5c68916fc 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -45,6 +45,7 @@ import { SettingsComponent } from './settings/settings.component';
import { TwoFactorAuthenticatorComponent } from './settings/two-factor-authenticator.component';
import { TwoFactorDuoComponent } from './settings/two-factor-duo.component';
import { TwoFactorEmailComponent } from './settings/two-factor-email.component';
+import { TwoFactorRecoveryComponent } from './settings/two-factor-recovery.component';
import { TwoFactorSetupComponent } from './settings/two-factor-setup.component';
import { TwoFactorU2fComponent } from './settings/two-factor-u2f.component';
import { TwoFactorVerifyComponent } from './settings/two-factor-verify.component';
@@ -154,10 +155,11 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
TwoFactorDuoComponent,
TwoFactorEmailComponent,
TwoFactorOptionsComponent,
+ TwoFactorRecoveryComponent,
+ TwoFactorSetupComponent,
TwoFactorU2fComponent,
TwoFactorVerifyComponent,
TwoFactorYubiKeyComponent,
- TwoFactorSetupComponent,
UserLayoutComponent,
VaultComponent,
],
@@ -179,6 +181,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
TwoFactorDuoComponent,
TwoFactorEmailComponent,
TwoFactorOptionsComponent,
+ TwoFactorRecoveryComponent,
TwoFactorU2fComponent,
TwoFactorYubiKeyComponent,
],
diff --git a/src/app/settings/two-factor-recovery.component.html b/src/app/settings/two-factor-recovery.component.html
new file mode 100644
index 0000000000..44dee6e718
--- /dev/null
+++ b/src/app/settings/two-factor-recovery.component.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
{{'twoFactorRecoveryYourCode' | i18n}}:
+
{{code}}
+
+
+
+
+
+
diff --git a/src/app/settings/two-factor-recovery.component.ts b/src/app/settings/two-factor-recovery.component.ts
new file mode 100644
index 0000000000..913e263551
--- /dev/null
+++ b/src/app/settings/two-factor-recovery.component.ts
@@ -0,0 +1,46 @@
+import { Component } from '@angular/core';
+
+import { I18nService } from 'jslib/abstractions/i18n.service';
+
+import { TwoFactorRecoverResponse } from 'jslib/models/response/twoFactorRescoverResponse';
+
+import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
+
+@Component({
+ selector: 'app-two-factor-recovery',
+ templateUrl: 'two-factor-recovery.component.html',
+})
+export class TwoFactorRecoveryComponent {
+ code: string;
+ authed: boolean;
+ twoFactorProviderType = TwoFactorProviderType;
+
+ constructor(private i18nService: I18nService) { }
+
+ auth(authResponse: any) {
+ this.authed = true;
+ this.processResponse(authResponse.response);
+ }
+
+ print() {
+ const w = window.open();
+ w.document.write('' +
+ '
' + this.i18nService.t('twoFactorRecoveryYourCode') + ':
' +
+ '
' +
+ this.code + '
' +
+ '' + new Date() + '
');
+ w.print();
+ w.close();
+ }
+
+ private formatString(s: string) {
+ if (s == null) {
+ return null;
+ }
+ return s.replace(/(.{4})/g, '$1 ').trim().toUpperCase();
+ }
+
+ private processResponse(response: TwoFactorRecoverResponse) {
+ this.code = this.formatString(response.code);
+ }
+}
diff --git a/src/app/settings/two-factor-setup.component.html b/src/app/settings/two-factor-setup.component.html
index a09d738b74..19ba71d93a 100644
--- a/src/app/settings/two-factor-setup.component.html
+++ b/src/app/settings/two-factor-setup.component.html
@@ -3,7 +3,7 @@
{{'twoStepLoginRecoveryWarning' | i18n}}
-
+
{{'providers' | i18n}}
diff --git a/src/app/settings/two-factor-setup.component.ts b/src/app/settings/two-factor-setup.component.ts
index f87afb6743..32403329b2 100644
--- a/src/app/settings/two-factor-setup.component.ts
+++ b/src/app/settings/two-factor-setup.component.ts
@@ -19,6 +19,7 @@ import { ModalComponent } from '../modal.component';
import { TwoFactorAuthenticatorComponent } from './two-factor-authenticator.component';
import { TwoFactorDuoComponent } from './two-factor-duo.component';
import { TwoFactorEmailComponent } from './two-factor-email.component';
+import { TwoFactorRecoveryComponent } from './two-factor-recovery.component';
import { TwoFactorU2fComponent } from './two-factor-u2f.component';
import { TwoFactorYubiKeyComponent } from './two-factor-yubikey.component';
@@ -120,6 +121,10 @@ export class TwoFactorSetupComponent implements OnInit {
}
}
+ recoveryCode() {
+ this.openModal(this.recoveryModalRef, TwoFactorRecoveryComponent);
+ }
+
private openModal(ref: ViewContainerRef, type: Type): T {
if (this.modal != null) {
this.modal.close();
diff --git a/src/app/settings/two-factor-verify.component.ts b/src/app/settings/two-factor-verify.component.ts
index 9e002342cb..5d8ce40bb3 100644
--- a/src/app/settings/two-factor-verify.component.ts
+++ b/src/app/settings/two-factor-verify.component.ts
@@ -46,6 +46,9 @@ export class TwoFactorVerifyComponent {
try {
switch (this.type) {
+ case -1:
+ this.formPromise = this.apiService.getTwoFactorRecover(request);
+ break;
case TwoFactorProviderType.Duo:
this.formPromise = this.apiService.getTwoFactorDuo(request);
break;
diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json
index d14cb72996..52e423316a 100644
--- a/src/locales/en/messages.json
+++ b/src/locales/en/messages.json
@@ -1125,5 +1125,12 @@
},
"twoFactorU2fProblemReading": {
"message": "There was a problem reading the security key."
+ },
+ "twoFactorRecoveryYourCode": {
+ "message": "Your Bitwarden two-step login recovery code"
+ },
+ "printCode": {
+ "message": "Print Code",
+ "description": "Print 2FA recovery code"
}
}
diff --git a/src/scss/styles.scss b/src/scss/styles.scss
index 6fde6f49b5..fecda1a402 100644
--- a/src/scss/styles.scss
+++ b/src/scss/styles.scss
@@ -378,6 +378,12 @@ app-avatar {
}
}
+app-two-factor-recovery {
+ code {
+ font-size: $font-size-lg;
+ }
+}
+
#duo-frame {
background: url('../images/loading.svg') 0 0 no-repeat;
height: 330px;