diff --git a/src/app/accounts/recover-two-factor.component.html b/src/app/accounts/recover-two-factor.component.html new file mode 100644 index 00000000..5fb4ae08 --- /dev/null +++ b/src/app/accounts/recover-two-factor.component.html @@ -0,0 +1,39 @@ +
+
+
+

{{'recoverAccountTwoStep' | i18n}}

+
+
+

{{'recoverAccountTwoStepDesc' | i18n}} + {{'learnMore' | i18n}} +

+
+ + +
+
+ + +
+
+ + +
+
+
+ + + {{'cancel' | i18n}} + +
+
+
+
+
+
diff --git a/src/app/accounts/recover-two-factor.component.ts b/src/app/accounts/recover-two-factor.component.ts new file mode 100644 index 00000000..72da951d --- /dev/null +++ b/src/app/accounts/recover-two-factor.component.ts @@ -0,0 +1,42 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { CryptoService } from 'jslib/abstractions/crypto.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +import { TwoFactorRecoveryRequest } from 'jslib/models/request/twoFactorRecoveryRequest'; + +@Component({ + selector: 'app-recover-two-factor', + templateUrl: 'recover-two-factor.component.html', +}) +export class RecoverTwoFactorComponent { + email: string; + masterPassword: string; + recoveryCode: string; + formPromise: Promise; + + constructor(private router: Router, private apiService: ApiService, + private analytics: Angulartics2, private toasterService: ToasterService, + private i18nService: I18nService, private cryptoService: CryptoService) { + } + + async submit() { + try { + const request = new TwoFactorRecoveryRequest(); + request.recoveryCode = this.recoveryCode.replace(/\s/g, '').toLowerCase(); + request.email = this.email.toLowerCase(); + const key = await this.cryptoService.makeKey(this.masterPassword, request.email); + request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key); + this.formPromise = this.apiService.postTwoFactorRecover(request); + await this.formPromise; + this.analytics.eventTrack.next({ action: 'Recovered 2FA' }); + this.toasterService.popAsync('success', null, this.i18nService.t('twoStepRecoverDisabled')); + this.router.navigate(['/']); + } catch { } + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d083e18e..d19914a6 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -12,6 +12,7 @@ import { AcceptOrganizationComponent } from './accounts/accept-organization.comp import { HintComponent } from './accounts/hint.component'; import { LockComponent } from './accounts/lock.component'; import { LoginComponent } from './accounts/login.component'; +import { RecoverTwoFactorComponent } from './accounts/recover-two-factor.component'; import { RegisterComponent } from './accounts/register.component'; import { TwoFactorComponent } from './accounts/two-factor.component'; import { VerifyEmailTokenComponent } from './accounts/verify-email-token.component'; @@ -74,6 +75,8 @@ const routes: Routes = [ { path: 'lock', component: LockComponent }, { path: 'verify-email', component: VerifyEmailTokenComponent }, { path: 'accept-organization', component: AcceptOrganizationComponent }, + { path: 'recover', pathMatch: 'full', redirectTo: 'recover-2fa' }, + { path: 'recover-2fa', component: RecoverTwoFactorComponent, canActivate: [UnauthGuardService] }, ], }, { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e3f1c63f..6f08250c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -29,6 +29,7 @@ import { AcceptOrganizationComponent } from './accounts/accept-organization.comp import { HintComponent } from './accounts/hint.component'; import { LockComponent } from './accounts/lock.component'; import { LoginComponent } from './accounts/login.component'; +import { RecoverTwoFactorComponent } from './accounts/recover-two-factor.component'; import { RegisterComponent } from './accounts/register.component'; import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component'; import { TwoFactorComponent } from './accounts/two-factor.component'; @@ -206,6 +207,7 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe'; PremiumComponent, ProfileComponent, PurgeVaultComponent, + RecoverTwoFactorComponent, RegisterComponent, SearchCiphersPipe, SearchPipe, diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 9faa4cd2..57fac440 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -2122,5 +2122,17 @@ }, "rememberEmail": { "message": "Remember email" + }, + "recoverAccountTwoStepDesc": { + "message": "If you cannot access your account through your normal two-step login methods, you can use your two-step login recovery code to disable all two-step providers on your account." + }, + "recoverAccountTwoStep": { + "message": "Recover Account Two-Step Login" + }, + "twoStepRecoverDisabled": { + "message": "Two-step login has been disabled on your account." + }, + "learnMore": { + "message": "Learn more" } }