org 2fa setting for duo
This commit is contained in:
parent
5131ebb9c9
commit
ee4d2400c9
2
jslib
2
jslib
|
@ -1 +1 @@
|
|||
Subproject commit f4ed6a5566b49e1a7175d931408bbe14e4dc0af7
|
||||
Subproject commit e555536f2413927508db91affa371560ba633c88
|
|
@ -28,6 +28,9 @@ import { PeopleComponent as OrgPeopleComponent } from './organizations/manage/pe
|
|||
import { AccountComponent as OrgAccountComponent } from './organizations/settings/account.component';
|
||||
import { OrganizationBillingComponent } from './organizations/settings/organization-billing.component';
|
||||
import { SettingsComponent as OrgSettingsComponent } from './organizations/settings/settings.component';
|
||||
import {
|
||||
TwoFactorSetupComponent as OrgTwoFactorSetupComponent,
|
||||
} from './organizations/settings/two-factor-setup.component';
|
||||
|
||||
import { ExportComponent as OrgExportComponent } from './organizations/tools/export.component';
|
||||
import { ImportComponent as OrgImportComponent } from './organizations/tools/import.component';
|
||||
|
@ -188,6 +191,7 @@ const routes: Routes = [
|
|||
children: [
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'account' },
|
||||
{ path: 'account', component: OrgAccountComponent, data: { titleId: 'myOrganization' } },
|
||||
{ path: 'two-factor', component: OrgTwoFactorSetupComponent, data: { titleId: 'twoStepLogin' } },
|
||||
{
|
||||
path: 'billing',
|
||||
component: OrganizationBillingComponent,
|
||||
|
|
|
@ -56,6 +56,9 @@ import { AdjustSeatsComponent } from './organizations/settings/adjust-seats.comp
|
|||
import { DeleteOrganizationComponent } from './organizations/settings/delete-organization.component';
|
||||
import { OrganizationBillingComponent } from './organizations/settings/organization-billing.component';
|
||||
import { SettingsComponent as OrgSettingComponent } from './organizations/settings/settings.component';
|
||||
import {
|
||||
TwoFactorSetupComponent as OrgTwoFactorSetupComponent,
|
||||
} from './organizations/settings/two-factor-setup.component';
|
||||
|
||||
import { ExportComponent as OrgExportComponent } from './organizations/tools/export.component';
|
||||
import { ImportComponent as OrgImportComponent } from './organizations/tools/import.component';
|
||||
|
@ -210,6 +213,7 @@ import { SearchPipe } from 'jslib/angular/pipes/search.pipe';
|
|||
OrgPeopleComponent,
|
||||
OrgSettingComponent,
|
||||
OrgToolsComponent,
|
||||
OrgTwoFactorSetupComponent,
|
||||
OrgUserAddEditComponent,
|
||||
OrgUserGroupsComponent,
|
||||
OrganizationsComponent,
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<a routerLink="billing" class="list-group-item" routerLinkActive="active">
|
||||
{{'billingAndLicensing' | i18n}}
|
||||
</a>
|
||||
<a routerLink="two-factor" class="list-group-item" routerLinkActive="active" *ngIf="access2fa">
|
||||
{{'twoStepLogin' | i18n}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,21 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-settings',
|
||||
templateUrl: 'settings.component.html',
|
||||
})
|
||||
export class SettingsComponent { }
|
||||
export class SettingsComponent {
|
||||
access2fa = false;
|
||||
|
||||
constructor(private route: ActivatedRoute, private userService: UserService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.route.parent.params.subscribe(async (params) => {
|
||||
const organization = await this.userService.getOrganization(params.organizationId);
|
||||
this.access2fa = organization.use2fa;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import {
|
||||
Component,
|
||||
ComponentFactoryResolver,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { ApiService } from 'jslib/abstractions/api.service';
|
||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||
import { TokenService } from 'jslib/abstractions/token.service';
|
||||
|
||||
import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
|
||||
|
||||
import { TwoFactorDuoComponent } from '../../settings/two-factor-duo.component';
|
||||
import { TwoFactorSetupComponent as BaseTwoFactorSetupComponent } from '../../settings/two-factor-setup.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-two-factor-setup',
|
||||
templateUrl: '../../settings/two-factor-setup.component.html',
|
||||
})
|
||||
export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent {
|
||||
organizationId: string;
|
||||
|
||||
constructor(apiService: ApiService, tokenService: TokenService,
|
||||
componentFactoryResolver: ComponentFactoryResolver, messagingService: MessagingService,
|
||||
private route: ActivatedRoute) {
|
||||
super(apiService, tokenService, componentFactoryResolver, messagingService);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organizationId = params.organizationId;
|
||||
await super.ngOnInit();
|
||||
});
|
||||
}
|
||||
|
||||
manage(type: TwoFactorProviderType) {
|
||||
switch (type) {
|
||||
case TwoFactorProviderType.OrganizationDuo:
|
||||
const duoComp = this.openModal(this.duoModalRef, TwoFactorDuoComponent);
|
||||
duoComp.type = TwoFactorProviderType.OrganizationDuo;
|
||||
duoComp.organizationId = this.organizationId;
|
||||
duoComp.onUpdated.subscribe((enabled: boolean) => {
|
||||
this.updateStatus(enabled, TwoFactorProviderType.OrganizationDuo);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected getTwoFactorProviders() {
|
||||
return this.apiService.getTwoFactorOrganizationProviders(this.organizationId);
|
||||
}
|
||||
|
||||
protected filterProvider(type: TwoFactorProviderType) {
|
||||
return type !== TwoFactorProviderType.OrganizationDuo;
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<app-two-factor-verify [type]="twoFactorProviderType.Authenticator" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
<app-two-factor-verify [organizationId]="organizationId" [type]="type" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
</app-two-factor-verify>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="authed">
|
||||
<div class="modal-body">
|
||||
|
|
|
@ -24,6 +24,7 @@ import { TwoFactorBaseComponent } from './two-factor-base.component';
|
|||
templateUrl: 'two-factor-authenticator.component.html',
|
||||
})
|
||||
export class TwoFactorAuthenticatorComponent extends TwoFactorBaseComponent implements OnInit, OnDestroy {
|
||||
type = TwoFactorProviderType.Authenticator;
|
||||
key: string;
|
||||
token: string;
|
||||
formPromise: Promise<any>;
|
||||
|
@ -33,8 +34,7 @@ export class TwoFactorAuthenticatorComponent extends TwoFactorBaseComponent impl
|
|||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
private userService: UserService, platformUtilsService: PlatformUtilsService) {
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService,
|
||||
TwoFactorProviderType.Authenticator);
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService);
|
||||
this.qrScript = window.document.createElement('script');
|
||||
this.qrScript.src = 'scripts/qrious.min.js';
|
||||
this.qrScript.async = true;
|
||||
|
|
|
@ -16,6 +16,8 @@ import { TwoFactorProviderRequest } from 'jslib/models/request/twoFactorProvider
|
|||
export abstract class TwoFactorBaseComponent {
|
||||
@Output() onUpdated = new EventEmitter<boolean>();
|
||||
|
||||
type: TwoFactorProviderType;
|
||||
organizationId: string;
|
||||
twoFactorProviderType = TwoFactorProviderType;
|
||||
enabled = false;
|
||||
authed = false;
|
||||
|
@ -24,7 +26,7 @@ export abstract class TwoFactorBaseComponent {
|
|||
|
||||
constructor(protected apiService: ApiService, protected i18nService: I18nService,
|
||||
protected analytics: Angulartics2, protected toasterService: ToasterService,
|
||||
protected platformUtilsService: PlatformUtilsService, private type: TwoFactorProviderType) { }
|
||||
protected platformUtilsService: PlatformUtilsService) { }
|
||||
|
||||
protected auth(authResponse: any) {
|
||||
this.masterPasswordHash = authResponse.masterPasswordHash;
|
||||
|
@ -52,7 +54,11 @@ export abstract class TwoFactorBaseComponent {
|
|||
const request = new TwoFactorProviderRequest();
|
||||
request.masterPasswordHash = this.masterPasswordHash;
|
||||
request.type = this.type;
|
||||
promise = this.apiService.putTwoFactorDisable(request);
|
||||
if (this.organizationId != null) {
|
||||
promise = this.apiService.putTwoFactorOrganizationDisable(this.organizationId, request);
|
||||
} else {
|
||||
promise = this.apiService.putTwoFactorDisable(request);
|
||||
}
|
||||
await promise;
|
||||
this.enabled = false;
|
||||
this.analytics.eventTrack.next({
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<app-two-factor-verify [type]="twoFactorProviderType.Duo" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
<app-two-factor-verify [organizationId]="organizationId" [type]="type" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
</app-two-factor-verify>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="authed">
|
||||
<div class="modal-body">
|
||||
|
|
|
@ -18,6 +18,7 @@ import { TwoFactorBaseComponent } from './two-factor-base.component';
|
|||
templateUrl: 'two-factor-duo.component.html',
|
||||
})
|
||||
export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
||||
type = TwoFactorProviderType.Duo;
|
||||
ikey: string;
|
||||
skey: string;
|
||||
host: string;
|
||||
|
@ -26,8 +27,7 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
|||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
platformUtilsService: PlatformUtilsService) {
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService,
|
||||
TwoFactorProviderType.Duo);
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
|
@ -51,7 +51,11 @@ export class TwoFactorDuoComponent extends TwoFactorBaseComponent {
|
|||
request.host = this.host;
|
||||
|
||||
return super.enable(async () => {
|
||||
this.formPromise = this.apiService.putTwoFactorDuo(request);
|
||||
if (this.organizationId != null) {
|
||||
this.formPromise = this.apiService.putTwoFactorOrganizationDuo(this.organizationId, request);
|
||||
} else {
|
||||
this.formPromise = this.apiService.putTwoFactorDuo(request);
|
||||
}
|
||||
const response = await this.formPromise;
|
||||
await this.processResponse(response);
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<app-two-factor-verify [type]="twoFactorProviderType.Email" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
<app-two-factor-verify [organizationId]="organizationId" [type]="type" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
</app-two-factor-verify>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="authed">
|
||||
<div class="modal-body">
|
||||
|
|
|
@ -21,6 +21,7 @@ import { TwoFactorBaseComponent } from './two-factor-base.component';
|
|||
templateUrl: 'two-factor-email.component.html',
|
||||
})
|
||||
export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
||||
type = TwoFactorProviderType.Email;
|
||||
email: string;
|
||||
token: string;
|
||||
sentEmail: string;
|
||||
|
@ -30,8 +31,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
|||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
platformUtilsService: PlatformUtilsService, private userService: UserService) {
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService,
|
||||
TwoFactorProviderType.Email);
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<app-two-factor-verify [type]="-1" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
<app-two-factor-verify [organizationId]="organizationId" [type]="type" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
</app-two-factor-verify>
|
||||
<ng-container *ngIf="authed">
|
||||
<div class="modal-body text-center">
|
||||
|
|
|
@ -11,6 +11,7 @@ import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType';
|
|||
templateUrl: 'two-factor-recovery.component.html',
|
||||
})
|
||||
export class TwoFactorRecoveryComponent {
|
||||
type = -1;
|
||||
code: string;
|
||||
authed: boolean;
|
||||
twoFactorProviderType = TwoFactorProviderType;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<div class="page-header">
|
||||
<h1>{{'twoStepLogin' | i18n}}</h1>
|
||||
</div>
|
||||
<app-callout type="warning">
|
||||
<p *ngIf="!organizationId">{{'twoStepLoginDesc' | i18n}}</p>
|
||||
<p *ngIf="organizationId">{{'twoStepLoginOrganizationDesc' | i18n}}</p>
|
||||
<app-callout type="warning" *ngIf="!organizationId">
|
||||
<p>{{'twoStepLoginRecoveryWarning' | i18n}}</p>
|
||||
<button type="button" class="btn btn-outline-secondary" (click)="recoveryCode()">{{'viewRecoveryCode' | i18n}}</button>
|
||||
</app-callout>
|
||||
<h2 class="mt-5">
|
||||
<h2 [ngClass]="{'mt-5':!organizationId}">
|
||||
{{'providers' | i18n}}
|
||||
<small *ngIf="loading">
|
||||
<i class="fa fa-spinner fa-spin fa-fw text-muted" title="{{'loading' | i18n}}"></i>
|
||||
|
|
|
@ -42,8 +42,8 @@ export class TwoFactorSetupComponent implements OnInit {
|
|||
|
||||
private modal: ModalComponent = null;
|
||||
|
||||
constructor(private apiService: ApiService, private tokenService: TokenService,
|
||||
private componentFactoryResolver: ComponentFactoryResolver, private messagingService: MessagingService) { }
|
||||
constructor(protected apiService: ApiService, protected tokenService: TokenService,
|
||||
protected componentFactoryResolver: ComponentFactoryResolver, protected messagingService: MessagingService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.premium = this.tokenService.getPremium();
|
||||
|
@ -54,7 +54,7 @@ export class TwoFactorSetupComponent implements OnInit {
|
|||
}
|
||||
|
||||
const p = (TwoFactorProviders as any)[key];
|
||||
if (p.type === TwoFactorProviderType.OrganizationDuo) {
|
||||
if (this.filterProvider(p.type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ export class TwoFactorSetupComponent implements OnInit {
|
|||
|
||||
async load() {
|
||||
this.loading = true;
|
||||
const providerList = await this.apiService.getTwoFactorProviders();
|
||||
const providerList = await this.getTwoFactorProviders();
|
||||
providerList.data.forEach((p) => {
|
||||
this.providers.forEach((p2) => {
|
||||
if (p.type === p2.type) {
|
||||
|
@ -134,7 +134,15 @@ export class TwoFactorSetupComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
private openModal<T>(ref: ViewContainerRef, type: Type<T>): T {
|
||||
protected getTwoFactorProviders() {
|
||||
return this.apiService.getTwoFactorProviders();
|
||||
}
|
||||
|
||||
protected filterProvider(type: TwoFactorProviderType) {
|
||||
return type === TwoFactorProviderType.OrganizationDuo;
|
||||
}
|
||||
|
||||
protected openModal<T>(ref: ViewContainerRef, type: Type<T>): T {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
@ -149,7 +157,7 @@ export class TwoFactorSetupComponent implements OnInit {
|
|||
return childComponent;
|
||||
}
|
||||
|
||||
private updateStatus(enabled: boolean, type: TwoFactorProviderType) {
|
||||
protected updateStatus(enabled: boolean, type: TwoFactorProviderType) {
|
||||
if (!enabled && this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<app-two-factor-verify [type]="twoFactorProviderType.U2f" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
<app-two-factor-verify [organizationId]="organizationId" [type]="type" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
</app-two-factor-verify>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="authed">
|
||||
<div class="modal-body">
|
||||
|
|
|
@ -22,6 +22,7 @@ import { TwoFactorBaseComponent } from './two-factor-base.component';
|
|||
templateUrl: 'two-factor-u2f.component.html',
|
||||
})
|
||||
export class TwoFactorU2fComponent extends TwoFactorBaseComponent implements OnInit, OnDestroy {
|
||||
type = TwoFactorProviderType.U2f;
|
||||
u2fChallenge: any;
|
||||
u2fError: boolean;
|
||||
u2fListening: boolean;
|
||||
|
@ -34,8 +35,7 @@ export class TwoFactorU2fComponent extends TwoFactorBaseComponent implements OnI
|
|||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
platformUtilsService: PlatformUtilsService) {
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService,
|
||||
TwoFactorProviderType.U2f);
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService);
|
||||
this.u2fScript = window.document.createElement('script');
|
||||
this.u2fScript.src = 'scripts/u2f.js';
|
||||
this.u2fScript.async = true;
|
||||
|
|
|
@ -20,10 +20,9 @@ import { PasswordVerificationRequest } from 'jslib/models/request/passwordVerifi
|
|||
templateUrl: 'two-factor-verify.component.html',
|
||||
})
|
||||
export class TwoFactorVerifyComponent {
|
||||
@Input()
|
||||
type: TwoFactorProviderType;
|
||||
@Output()
|
||||
onAuthed = new EventEmitter<any>();
|
||||
@Input() type: TwoFactorProviderType;
|
||||
@Input() organizationId: string;
|
||||
@Output() onAuthed = new EventEmitter<any>();
|
||||
|
||||
masterPassword: string;
|
||||
formPromise: Promise<any>;
|
||||
|
@ -50,7 +49,12 @@ export class TwoFactorVerifyComponent {
|
|||
this.formPromise = this.apiService.getTwoFactorRecover(request);
|
||||
break;
|
||||
case TwoFactorProviderType.Duo:
|
||||
this.formPromise = this.apiService.getTwoFactorDuo(request);
|
||||
case TwoFactorProviderType.OrganizationDuo:
|
||||
if (this.organizationId != null) {
|
||||
this.formPromise = this.apiService.getTwoFactorOrganizationDuo(this.organizationId, request);
|
||||
} else {
|
||||
this.formPromise = this.apiService.getTwoFactorDuo(request);
|
||||
}
|
||||
break;
|
||||
case TwoFactorProviderType.Email:
|
||||
this.formPromise = this.apiService.getTwoFactorEmail(request);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<app-two-factor-verify [type]="twoFactorProviderType.Yubikey" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
<app-two-factor-verify [organizationId]="organizationId" [type]="type" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
</app-two-factor-verify>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate *ngIf="authed">
|
||||
<div class="modal-body">
|
||||
|
|
|
@ -19,6 +19,7 @@ import { TwoFactorBaseComponent } from './two-factor-base.component';
|
|||
templateUrl: 'two-factor-yubikey.component.html',
|
||||
})
|
||||
export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent {
|
||||
type = TwoFactorProviderType.Yubikey;
|
||||
keys: any[];
|
||||
nfc = false;
|
||||
|
||||
|
@ -28,8 +29,7 @@ export class TwoFactorYubiKeyComponent extends TwoFactorBaseComponent {
|
|||
constructor(apiService: ApiService, i18nService: I18nService,
|
||||
analytics: Angulartics2, toasterService: ToasterService,
|
||||
platformUtilsService: PlatformUtilsService) {
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService,
|
||||
TwoFactorProviderType.Yubikey);
|
||||
super(apiService, i18nService, analytics, toasterService, platformUtilsService);
|
||||
}
|
||||
|
||||
auth(authResponse: any) {
|
||||
|
|
|
@ -1003,6 +1003,12 @@
|
|||
"twoStepLogin": {
|
||||
"message": "Two-step Login"
|
||||
},
|
||||
"twoStepLoginDesc": {
|
||||
"message": "Secure your account by requiring an additional step when logging in."
|
||||
},
|
||||
"twoStepLoginOrganizationDesc": {
|
||||
"message": "Require two-step login for your organization's users by configuring providers at the organization level."
|
||||
},
|
||||
"twoStepLoginRecoveryWarning": {
|
||||
"message": "Enabling two-step login can permanently lock you out of your Bitwarden account. A recovery code allows you to access your account in the event that you can no longer use your normal two-step login provider (ex. you lose your device). Bitwarden support will not be able to assist you if you lose access to your account. We recommend you write down or print the recovery code and keep it in a safe place."
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue