mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
refactor(auth): [PM-8976] migrate two-factor setup component to Tailwind and standalone
- Remove Bootstrap styles from two-factor-setup component and replace with Tailwind equivalents - Convert two factor components to standalone components to move away from LooseComponents - Replace ul/li list with bit-item-group and bit-item components - Integrate with the bit design system --------- Co-authored-by: Oscar Hinton <Hinton@users.noreply.github.com> Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -21,11 +21,3 @@
|
|||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.recovery-code-img {
|
|
||||||
@include themify($themes) {
|
|
||||||
content: url("../images/two-factor/rc" + themed("mfaLogoSuffix"));
|
|
||||||
max-width: 100px;
|
|
||||||
max-height: 45px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -21,11 +21,3 @@
|
|||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.recovery-code-img {
|
|
||||||
@include themify($themes) {
|
|
||||||
content: url("../images/two-factor/rc" + themed("mfaLogoSuffix"));
|
|
||||||
max-width: 100px;
|
|
||||||
max-height: 45px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { NgModule } from "@angular/core";
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
|
import { ItemModule } from "@bitwarden/components";
|
||||||
|
|
||||||
import { LooseComponentsModule, SharedModule } from "../../../shared";
|
import { LooseComponentsModule, SharedModule } from "../../../shared";
|
||||||
import { AccountFingerprintComponent } from "../../../shared/components/account-fingerprint/account-fingerprint.component";
|
import { AccountFingerprintComponent } from "../../../shared/components/account-fingerprint/account-fingerprint.component";
|
||||||
import { PoliciesModule } from "../../organizations/policies";
|
import { PoliciesModule } from "../../organizations/policies";
|
||||||
@@ -15,6 +17,7 @@ import { TwoFactorSetupComponent } from "./two-factor-setup.component";
|
|||||||
PoliciesModule,
|
PoliciesModule,
|
||||||
OrganizationSettingsRoutingModule,
|
OrganizationSettingsRoutingModule,
|
||||||
AccountFingerprintComponent,
|
AccountFingerprintComponent,
|
||||||
|
ItemModule,
|
||||||
],
|
],
|
||||||
declarations: [AccountComponent, TwoFactorSetupComponent],
|
declarations: [AccountComponent, TwoFactorSetupComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,36 +1,50 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { CommonModule } from "@angular/common";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, Inject } from "@angular/core";
|
import { Component, Inject } from "@angular/core";
|
||||||
|
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
import { TwoFactorRecoverResponse } from "@bitwarden/common/auth/models/response/two-factor-recover.response";
|
import { TwoFactorRecoverResponse } from "@bitwarden/common/auth/models/response/two-factor-recover.response";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { DIALOG_DATA, DialogConfig, DialogService } from "@bitwarden/components";
|
import {
|
||||||
|
ButtonModule,
|
||||||
|
DIALOG_DATA,
|
||||||
|
DialogConfig,
|
||||||
|
DialogModule,
|
||||||
|
DialogRef,
|
||||||
|
DialogService,
|
||||||
|
TypographyModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-recovery",
|
selector: "app-two-factor-recovery",
|
||||||
templateUrl: "two-factor-recovery.component.html",
|
templateUrl: "two-factor-recovery.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, DialogModule, ButtonModule, TypographyModule, I18nPipe],
|
||||||
})
|
})
|
||||||
export class TwoFactorRecoveryComponent {
|
export class TwoFactorRecoveryComponent {
|
||||||
type = -1;
|
type = -1;
|
||||||
code: string;
|
code: string = "";
|
||||||
authed: boolean;
|
authed: boolean = false;
|
||||||
twoFactorProviderType = TwoFactorProviderType;
|
twoFactorProviderType = TwoFactorProviderType;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DIALOG_DATA) protected data: any,
|
@Inject(DIALOG_DATA) protected data: { response: { response: TwoFactorRecoverResponse } },
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
) {
|
) {
|
||||||
this.auth(data.response);
|
this.auth(data.response);
|
||||||
}
|
}
|
||||||
|
|
||||||
auth(authResponse: any) {
|
auth(authResponse: { response: TwoFactorRecoverResponse }) {
|
||||||
this.authed = true;
|
this.authed = true;
|
||||||
this.processResponse(authResponse.response);
|
this.processResponse(authResponse.response);
|
||||||
}
|
}
|
||||||
|
|
||||||
print() {
|
print() {
|
||||||
const w = window.open();
|
const w = window.open();
|
||||||
|
if (!w) {
|
||||||
|
// return early if the window is not open
|
||||||
|
return;
|
||||||
|
}
|
||||||
w.document.write(
|
w.document.write(
|
||||||
'<div style="font-size: 18px; text-align: center;">' +
|
'<div style="font-size: 18px; text-align: center;">' +
|
||||||
"<p>" +
|
"<p>" +
|
||||||
@@ -47,9 +61,9 @@ export class TwoFactorRecoveryComponent {
|
|||||||
w.print();
|
w.print();
|
||||||
}
|
}
|
||||||
|
|
||||||
private formatString(s: string) {
|
private formatString(s: string): string {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
.replace(/(.{4})/g, "$1 ")
|
.replace(/(.{4})/g, "$1 ")
|
||||||
@@ -61,7 +75,13 @@ export class TwoFactorRecoveryComponent {
|
|||||||
this.code = this.formatString(response.code);
|
this.code = this.formatString(response.code);
|
||||||
}
|
}
|
||||||
|
|
||||||
static open(dialogService: DialogService, config: DialogConfig<any>) {
|
static open(
|
||||||
|
dialogService: DialogService,
|
||||||
|
config: DialogConfig<
|
||||||
|
{ response: { response: TwoFactorRecoverResponse } },
|
||||||
|
DialogRef<unknown, TwoFactorRecoveryComponent>
|
||||||
|
>,
|
||||||
|
) {
|
||||||
return dialogService.open(TwoFactorRecoveryComponent, config);
|
return dialogService.open(TwoFactorRecoveryComponent, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
{{ "twoStepAuthenticatorInstructionSuffix" | i18n }}
|
{{ "twoStepAuthenticatorInstructionSuffix" | i18n }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="text-center">
|
<p class="tw-text-center">
|
||||||
<a
|
<a
|
||||||
href="https://apps.apple.com/ca/app/bitwarden-authenticator/id6497335175"
|
href="https://apps.apple.com/ca/app/bitwarden-authenticator/id6497335175"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, EventEmitter, Inject, OnDestroy, OnInit, Output } from "@angular/core";
|
import { Component, EventEmitter, Inject, OnDestroy, OnInit, Output } from "@angular/core";
|
||||||
import { FormBuilder, FormControl, Validators } from "@angular/forms";
|
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
@@ -18,12 +20,22 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
|||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import {
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CalloutModule,
|
||||||
DIALOG_DATA,
|
DIALOG_DATA,
|
||||||
DialogConfig,
|
DialogConfig,
|
||||||
|
DialogModule,
|
||||||
DialogRef,
|
DialogRef,
|
||||||
DialogService,
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
IconModule,
|
||||||
|
InputModule,
|
||||||
|
LinkModule,
|
||||||
ToastService,
|
ToastService,
|
||||||
|
TypographyModule,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
||||||
|
|
||||||
@@ -44,6 +56,22 @@ declare global {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-setup-authenticator",
|
selector: "app-two-factor-setup-authenticator",
|
||||||
templateUrl: "two-factor-setup-authenticator.component.html",
|
templateUrl: "two-factor-setup-authenticator.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
DialogModule,
|
||||||
|
FormFieldModule,
|
||||||
|
InputModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
CalloutModule,
|
||||||
|
ButtonModule,
|
||||||
|
IconModule,
|
||||||
|
I18nPipe,
|
||||||
|
AsyncActionsModule,
|
||||||
|
JslibModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class TwoFactorSetupAuthenticatorComponent
|
export class TwoFactorSetupAuthenticatorComponent
|
||||||
extends TwoFactorSetupMethodBaseComponent
|
extends TwoFactorSetupMethodBaseComponent
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
<bit-dialog [title]="'twoStepLogin' | i18n" [subtitle]="'Duo'">
|
<bit-dialog [title]="'twoStepLogin' | i18n" [subtitle]="'Duo'">
|
||||||
<ng-container bitDialogContent>
|
<ng-container bitDialogContent>
|
||||||
<ng-container *ngIf="enabled">
|
<ng-container *ngIf="enabled">
|
||||||
<app-callout type="success" title="{{ 'enabled' | i18n }}" icon="bwi bwi-check-circle">
|
<bit-callout type="success" [title]="'enabled' | i18n" icon="bwi bwi-check-circle">
|
||||||
{{ "twoStepLoginProviderEnabled" | i18n }}
|
{{ "twoStepLoginProviderEnabled" | i18n }}
|
||||||
</app-callout>
|
</bit-callout>
|
||||||
<img class="tw-float-right tw-ml-3 mfaType2" alt="Duo logo" />
|
<img class="tw-float-right tw-ml-3 mfaType2" alt="Duo logo" />
|
||||||
<strong>{{ "twoFactorDuoClientId" | i18n }}:</strong> {{ clientId }}
|
<strong>{{ "twoFactorDuoClientId" | i18n }}:</strong> {{ clientId }}
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { CommonModule } from "@angular/common";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, EventEmitter, Inject, OnInit, Output } from "@angular/core";
|
import { Component, EventEmitter, Inject, OnInit, Output } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
@@ -13,18 +12,41 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import {
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CalloutModule,
|
||||||
DIALOG_DATA,
|
DIALOG_DATA,
|
||||||
DialogConfig,
|
DialogConfig,
|
||||||
|
DialogModule,
|
||||||
DialogRef,
|
DialogRef,
|
||||||
DialogService,
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
IconModule,
|
||||||
|
InputModule,
|
||||||
ToastService,
|
ToastService,
|
||||||
|
TypographyModule,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-setup-duo",
|
selector: "app-two-factor-setup-duo",
|
||||||
templateUrl: "two-factor-setup-duo.component.html",
|
templateUrl: "two-factor-setup-duo.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
DialogModule,
|
||||||
|
FormFieldModule,
|
||||||
|
InputModule,
|
||||||
|
TypographyModule,
|
||||||
|
ButtonModule,
|
||||||
|
IconModule,
|
||||||
|
I18nPipe,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
CalloutModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class TwoFactorSetupDuoComponent
|
export class TwoFactorSetupDuoComponent
|
||||||
extends TwoFactorSetupMethodBaseComponent
|
extends TwoFactorSetupMethodBaseComponent
|
||||||
@@ -63,23 +85,23 @@ export class TwoFactorSetupDuoComponent
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get clientId() {
|
get clientId(): string {
|
||||||
return this.formGroup.get("clientId").value;
|
return this.formGroup.get("clientId")?.value || "";
|
||||||
}
|
}
|
||||||
get clientSecret() {
|
get clientSecret(): string {
|
||||||
return this.formGroup.get("clientSecret").value;
|
return this.formGroup.get("clientSecret")?.value || "";
|
||||||
}
|
}
|
||||||
get host() {
|
get host(): string {
|
||||||
return this.formGroup.get("host").value;
|
return this.formGroup.get("host")?.value || "";
|
||||||
}
|
}
|
||||||
set clientId(value: string) {
|
set clientId(value: string) {
|
||||||
this.formGroup.get("clientId").setValue(value);
|
this.formGroup.get("clientId")?.setValue(value);
|
||||||
}
|
}
|
||||||
set clientSecret(value: string) {
|
set clientSecret(value: string) {
|
||||||
this.formGroup.get("clientSecret").setValue(value);
|
this.formGroup.get("clientSecret")?.setValue(value);
|
||||||
}
|
}
|
||||||
set host(value: string) {
|
set host(value: string) {
|
||||||
this.formGroup.get("host").setValue(value);
|
this.formGroup.get("host")?.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@@ -147,7 +169,10 @@ export class TwoFactorSetupDuoComponent
|
|||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
config: DialogConfig<TwoFactorDuoComponentConfig>,
|
config: DialogConfig<TwoFactorDuoComponentConfig>,
|
||||||
) => {
|
) => {
|
||||||
return dialogService.open<boolean>(TwoFactorSetupDuoComponent, config);
|
return dialogService.open<boolean, TwoFactorDuoComponentConfig>(
|
||||||
|
TwoFactorSetupDuoComponent,
|
||||||
|
config as DialogConfig<TwoFactorDuoComponentConfig, DialogRef<boolean>>,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { CommonModule } from "@angular/common";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, EventEmitter, Inject, OnInit, Output } from "@angular/core";
|
import { Component, EventEmitter, Inject, OnInit, Output } from "@angular/core";
|
||||||
import { FormBuilder, Validators } from "@angular/forms";
|
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -16,19 +15,41 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import {
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CalloutModule,
|
||||||
DIALOG_DATA,
|
DIALOG_DATA,
|
||||||
DialogConfig,
|
DialogConfig,
|
||||||
|
DialogModule,
|
||||||
DialogRef,
|
DialogRef,
|
||||||
DialogService,
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
IconModule,
|
||||||
|
InputModule,
|
||||||
ToastService,
|
ToastService,
|
||||||
|
TypographyModule,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-setup-email",
|
selector: "app-two-factor-setup-email",
|
||||||
templateUrl: "two-factor-setup-email.component.html",
|
templateUrl: "two-factor-setup-email.component.html",
|
||||||
outputs: ["onUpdated"],
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CalloutModule,
|
||||||
|
CommonModule,
|
||||||
|
DialogModule,
|
||||||
|
FormFieldModule,
|
||||||
|
IconModule,
|
||||||
|
I18nPipe,
|
||||||
|
InputModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
TypographyModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class TwoFactorSetupEmailComponent
|
export class TwoFactorSetupEmailComponent
|
||||||
extends TwoFactorSetupMethodBaseComponent
|
extends TwoFactorSetupMethodBaseComponent
|
||||||
@@ -36,8 +57,8 @@ export class TwoFactorSetupEmailComponent
|
|||||||
{
|
{
|
||||||
@Output() onChangeStatus: EventEmitter<boolean> = new EventEmitter();
|
@Output() onChangeStatus: EventEmitter<boolean> = new EventEmitter();
|
||||||
type = TwoFactorProviderType.Email;
|
type = TwoFactorProviderType.Email;
|
||||||
sentEmail: string;
|
sentEmail: string = "";
|
||||||
emailPromise: Promise<unknown>;
|
emailPromise: Promise<unknown> | undefined;
|
||||||
override componentName = "app-two-factor-email";
|
override componentName = "app-two-factor-email";
|
||||||
formGroup = this.formBuilder.group({
|
formGroup = this.formBuilder.group({
|
||||||
token: ["", [Validators.required]],
|
token: ["", [Validators.required]],
|
||||||
@@ -67,17 +88,17 @@ export class TwoFactorSetupEmailComponent
|
|||||||
toastService,
|
toastService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
get token() {
|
get token(): string {
|
||||||
return this.formGroup.get("token").value;
|
return this.formGroup.get("token")?.value || "";
|
||||||
}
|
}
|
||||||
set token(value: string) {
|
set token(value: string | null) {
|
||||||
this.formGroup.get("token").setValue(value);
|
this.formGroup.get("token")?.setValue(value || "");
|
||||||
}
|
}
|
||||||
get email() {
|
get email(): string {
|
||||||
return this.formGroup.get("email").value;
|
return this.formGroup.get("email")?.value || "";
|
||||||
}
|
}
|
||||||
set email(value: string) {
|
set email(value: string | null | undefined) {
|
||||||
this.formGroup.get("email").setValue(value);
|
this.formGroup.get("email")?.setValue(value || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@@ -149,6 +170,9 @@ export class TwoFactorSetupEmailComponent
|
|||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
config: DialogConfig<AuthResponse<TwoFactorEmailResponse>>,
|
config: DialogConfig<AuthResponse<TwoFactorEmailResponse>>,
|
||||||
) {
|
) {
|
||||||
return dialogService.open<boolean>(TwoFactorSetupEmailComponent, config);
|
return dialogService.open<boolean, AuthResponse<TwoFactorEmailResponse>>(
|
||||||
|
TwoFactorSetupEmailComponent,
|
||||||
|
config as DialogConfig<AuthResponse<TwoFactorEmailResponse>, DialogRef<boolean>>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Directive, EventEmitter, Output } from "@angular/core";
|
import { Directive, EventEmitter, Output } from "@angular/core";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -17,18 +15,20 @@ import { DialogService, ToastService } from "@bitwarden/components";
|
|||||||
/**
|
/**
|
||||||
* Base class for two-factor setup components (ex: email, yubikey, webauthn, duo).
|
* Base class for two-factor setup components (ex: email, yubikey, webauthn, duo).
|
||||||
*/
|
*/
|
||||||
@Directive()
|
@Directive({
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
export abstract class TwoFactorSetupMethodBaseComponent {
|
export abstract class TwoFactorSetupMethodBaseComponent {
|
||||||
@Output() onUpdated = new EventEmitter<boolean>();
|
@Output() onUpdated = new EventEmitter<boolean>();
|
||||||
|
|
||||||
type: TwoFactorProviderType;
|
type: TwoFactorProviderType | undefined;
|
||||||
organizationId: string;
|
organizationId: string | null = null;
|
||||||
twoFactorProviderType = TwoFactorProviderType;
|
twoFactorProviderType = TwoFactorProviderType;
|
||||||
enabled = false;
|
enabled = false;
|
||||||
authed = false;
|
authed = false;
|
||||||
|
|
||||||
protected hashedSecret: string;
|
protected hashedSecret: string | undefined;
|
||||||
protected verificationType: VerificationType;
|
protected verificationType: VerificationType | undefined;
|
||||||
protected componentName = "";
|
protected componentName = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -74,6 +74,9 @@ export abstract class TwoFactorSetupMethodBaseComponent {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const request = await this.buildRequestModel(TwoFactorProviderRequest);
|
const request = await this.buildRequestModel(TwoFactorProviderRequest);
|
||||||
|
if (this.type === undefined) {
|
||||||
|
throw new Error("Two-factor provider type is required");
|
||||||
|
}
|
||||||
request.type = this.type;
|
request.type = this.type;
|
||||||
if (this.organizationId != null) {
|
if (this.organizationId != null) {
|
||||||
promise = this.apiService.putTwoFactorOrganizationDisable(this.organizationId, request);
|
promise = this.apiService.putTwoFactorOrganizationDisable(this.organizationId, request);
|
||||||
@@ -84,7 +87,7 @@ export abstract class TwoFactorSetupMethodBaseComponent {
|
|||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: "",
|
||||||
message: this.i18nService.t("twoStepDisabled"),
|
message: this.i18nService.t("twoStepDisabled"),
|
||||||
});
|
});
|
||||||
this.onUpdated.emit(false);
|
this.onUpdated.emit(false);
|
||||||
@@ -105,6 +108,9 @@ export abstract class TwoFactorSetupMethodBaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const request = await this.buildRequestModel(TwoFactorProviderRequest);
|
const request = await this.buildRequestModel(TwoFactorProviderRequest);
|
||||||
|
if (this.type === undefined) {
|
||||||
|
throw new Error("Two-factor provider type is required");
|
||||||
|
}
|
||||||
request.type = this.type;
|
request.type = this.type;
|
||||||
if (this.organizationId != null) {
|
if (this.organizationId != null) {
|
||||||
await this.apiService.putTwoFactorOrganizationDisable(this.organizationId, request);
|
await this.apiService.putTwoFactorOrganizationDisable(this.organizationId, request);
|
||||||
@@ -114,7 +120,7 @@ export abstract class TwoFactorSetupMethodBaseComponent {
|
|||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: "",
|
||||||
message: this.i18nService.t("twoStepDisabled"),
|
message: this.i18nService.t("twoStepDisabled"),
|
||||||
});
|
});
|
||||||
this.onUpdated.emit(false);
|
this.onUpdated.emit(false);
|
||||||
@@ -123,6 +129,9 @@ export abstract class TwoFactorSetupMethodBaseComponent {
|
|||||||
protected async buildRequestModel<T extends SecretVerificationRequest>(
|
protected async buildRequestModel<T extends SecretVerificationRequest>(
|
||||||
requestClass: new () => T,
|
requestClass: new () => T,
|
||||||
) {
|
) {
|
||||||
|
if (this.hashedSecret === undefined || this.verificationType === undefined) {
|
||||||
|
throw new Error("User verification data is missing");
|
||||||
|
}
|
||||||
return this.userVerificationService.buildRequest(
|
return this.userVerificationService.buildRequest(
|
||||||
{
|
{
|
||||||
secret: this.hashedSecret,
|
secret: this.hashedSecret,
|
||||||
|
|||||||
@@ -5,23 +5,23 @@
|
|||||||
[subtitle]="'webAuthnTitle' | i18n"
|
[subtitle]="'webAuthnTitle' | i18n"
|
||||||
>
|
>
|
||||||
<ng-container bitDialogContent>
|
<ng-container bitDialogContent>
|
||||||
<app-callout
|
<bit-callout
|
||||||
type="success"
|
type="success"
|
||||||
title="{{ 'enabled' | i18n }}"
|
title="{{ 'enabled' | i18n }}"
|
||||||
icon="bwi bwi-check-circle"
|
icon="bwi bwi-check-circle"
|
||||||
*ngIf="enabled"
|
*ngIf="enabled"
|
||||||
>
|
>
|
||||||
{{ "twoStepLoginProviderEnabled" | i18n }}
|
{{ "twoStepLoginProviderEnabled" | i18n }}
|
||||||
</app-callout>
|
</bit-callout>
|
||||||
<app-callout type="warning">
|
<bit-callout type="warning">
|
||||||
<p bitTypography="body1">{{ "twoFactorWebAuthnWarning1" | i18n }}</p>
|
<p bitTypography="body1">{{ "twoFactorWebAuthnWarning1" | i18n }}</p>
|
||||||
</app-callout>
|
</bit-callout>
|
||||||
<img class="tw-float-right tw-ml-5 mfaType7" alt="FIDO2 WebAuthn logo" />
|
<img class="tw-float-right tw-ml-5 mfaType7" alt="FIDO2 WebAuthn logo" />
|
||||||
<ul class="bwi-ul">
|
<ul class="bwi-ul">
|
||||||
<li *ngFor="let k of keys; let i = index" #removeKeyBtn [appApiAction]="k.removePromise">
|
<li *ngFor="let k of keys; let i = index" #removeKeyBtn [appApiAction]="k.removePromise">
|
||||||
<i class="bwi bwi-li bwi-key"></i>
|
<i class="bwi bwi-li bwi-key"></i>
|
||||||
<span *ngIf="!k.configured || !k.name" bitTypography="body1" class="tw-font-bold">
|
<span *ngIf="!k.configured || !k.name" bitTypography="body1" class="tw-font-bold">
|
||||||
{{ "webAuthnkeyX" | i18n: i + 1 }}
|
{{ "webAuthnkeyX" | i18n: (i + 1).toString() }}
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="k.configured && k.name" bitTypography="body1" class="tw-font-bold">
|
<span *ngIf="k.configured && k.name" bitTypography="body1" class="tw-font-bold">
|
||||||
{{ k.name }}
|
{{ k.name }}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { CommonModule } from "@angular/common";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, Inject, NgZone } from "@angular/core";
|
import { Component, Inject, NgZone } from "@angular/core";
|
||||||
import { FormControl, FormGroup } from "@angular/forms";
|
import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
@@ -18,12 +18,20 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import {
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CalloutModule,
|
||||||
DIALOG_DATA,
|
DIALOG_DATA,
|
||||||
DialogConfig,
|
DialogConfig,
|
||||||
|
DialogModule,
|
||||||
DialogRef,
|
DialogRef,
|
||||||
DialogService,
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
LinkModule,
|
||||||
ToastService,
|
ToastService,
|
||||||
|
TypographyModule,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
||||||
|
|
||||||
@@ -38,24 +46,36 @@ interface Key {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-setup-webauthn",
|
selector: "app-two-factor-setup-webauthn",
|
||||||
templateUrl: "two-factor-setup-webauthn.component.html",
|
templateUrl: "two-factor-setup-webauthn.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CalloutModule,
|
||||||
|
CommonModule,
|
||||||
|
DialogModule,
|
||||||
|
FormFieldModule,
|
||||||
|
I18nPipe,
|
||||||
|
JslibModule,
|
||||||
|
LinkModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
TypographyModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseComponent {
|
export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseComponent {
|
||||||
type = TwoFactorProviderType.WebAuthn;
|
type = TwoFactorProviderType.WebAuthn;
|
||||||
name: string;
|
name: string = "";
|
||||||
keys: Key[];
|
keys: Key[] = [];
|
||||||
keyIdAvailable: number = null;
|
keyIdAvailable: number | null = null;
|
||||||
keysConfiguredCount = 0;
|
keysConfiguredCount = 0;
|
||||||
webAuthnError: boolean;
|
webAuthnError: boolean = false;
|
||||||
webAuthnListening: boolean;
|
webAuthnListening: boolean = false;
|
||||||
webAuthnResponse: PublicKeyCredential;
|
webAuthnResponse: PublicKeyCredential | null = null;
|
||||||
challengePromise: Promise<ChallengeResponse>;
|
challengePromise: Promise<ChallengeResponse> | undefined;
|
||||||
formPromise: Promise<TwoFactorWebAuthnResponse>;
|
formPromise: Promise<TwoFactorWebAuthnResponse> | undefined;
|
||||||
|
|
||||||
override componentName = "app-two-factor-webauthn";
|
override componentName = "app-two-factor-webauthn";
|
||||||
|
|
||||||
protected formGroup = new FormGroup({
|
protected formGroup: FormGroup;
|
||||||
name: new FormControl({ value: "", disabled: !this.keyIdAvailable }),
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DIALOG_DATA) protected data: AuthResponse<TwoFactorWebAuthnResponse>,
|
@Inject(DIALOG_DATA) protected data: AuthResponse<TwoFactorWebAuthnResponse>,
|
||||||
@@ -78,6 +98,9 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom
|
|||||||
dialogService,
|
dialogService,
|
||||||
toastService,
|
toastService,
|
||||||
);
|
);
|
||||||
|
this.formGroup = new FormGroup({
|
||||||
|
name: new FormControl({ value: "", disabled: false }),
|
||||||
|
});
|
||||||
this.auth(data);
|
this.auth(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,9 +119,14 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom
|
|||||||
|
|
||||||
protected async enable() {
|
protected async enable() {
|
||||||
const request = await this.buildRequestModel(UpdateTwoFactorWebAuthnRequest);
|
const request = await this.buildRequestModel(UpdateTwoFactorWebAuthnRequest);
|
||||||
|
|
||||||
|
if (this.webAuthnResponse == undefined || this.keyIdAvailable == undefined) {
|
||||||
|
throw new Error("WebAuthn response or key ID is missing");
|
||||||
|
}
|
||||||
|
|
||||||
request.deviceResponse = this.webAuthnResponse;
|
request.deviceResponse = this.webAuthnResponse;
|
||||||
request.id = this.keyIdAvailable;
|
request.id = this.keyIdAvailable;
|
||||||
request.name = this.formGroup.value.name;
|
request.name = this.formGroup.value.name || "";
|
||||||
|
|
||||||
const response = await this.apiService.putTwoFactorWebAuthn(request);
|
const response = await this.apiService.putTwoFactorWebAuthn(request);
|
||||||
this.processResponse(response);
|
this.processResponse(response);
|
||||||
@@ -164,10 +192,10 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom
|
|||||||
.create({
|
.create({
|
||||||
publicKey: webAuthnChallenge,
|
publicKey: webAuthnChallenge,
|
||||||
})
|
})
|
||||||
.then((data: PublicKeyCredential) => {
|
.then((data) => {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
this.webAuthnListening = false;
|
this.webAuthnListening = false;
|
||||||
this.webAuthnResponse = data;
|
this.webAuthnResponse = data as PublicKeyCredential;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@@ -189,8 +217,11 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom
|
|||||||
this.resetWebAuthn();
|
this.resetWebAuthn();
|
||||||
this.keys = [];
|
this.keys = [];
|
||||||
this.keyIdAvailable = null;
|
this.keyIdAvailable = null;
|
||||||
this.formGroup.get("name").enable();
|
const nameControl = this.formGroup.get("name");
|
||||||
this.formGroup.get("name").setValue(null);
|
if (nameControl) {
|
||||||
|
nameControl.enable();
|
||||||
|
nameControl.setValue("");
|
||||||
|
}
|
||||||
this.keysConfiguredCount = 0;
|
this.keysConfiguredCount = 0;
|
||||||
for (let i = 1; i <= 5; i++) {
|
for (let i = 1; i <= 5; i++) {
|
||||||
if (response.keys != null) {
|
if (response.keys != null) {
|
||||||
@@ -207,7 +238,7 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.keys.push({ id: i, name: null, configured: false, removePromise: null });
|
this.keys.push({ id: i, name: "", configured: false, removePromise: null });
|
||||||
if (this.keyIdAvailable == null) {
|
if (this.keyIdAvailable == null) {
|
||||||
this.keyIdAvailable = i;
|
this.keyIdAvailable = i;
|
||||||
}
|
}
|
||||||
@@ -220,6 +251,9 @@ export class TwoFactorSetupWebAuthnComponent extends TwoFactorSetupMethodBaseCom
|
|||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
config: DialogConfig<AuthResponse<TwoFactorWebAuthnResponse>>,
|
config: DialogConfig<AuthResponse<TwoFactorWebAuthnResponse>>,
|
||||||
) {
|
) {
|
||||||
return dialogService.open<boolean>(TwoFactorSetupWebAuthnComponent, config);
|
return dialogService.open<boolean, AuthResponse<TwoFactorWebAuthnResponse>>(
|
||||||
|
TwoFactorSetupWebAuthnComponent,
|
||||||
|
config as DialogConfig<AuthResponse<TwoFactorWebAuthnResponse>, DialogRef<boolean>>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<form *ngIf="authed" [formGroup]="formGroup" [bitSubmit]="submit">
|
<form *ngIf="authed" [formGroup]="formGroup" [bitSubmit]="submit">
|
||||||
<bit-dialog dialogSize="large" [title]="'twoStepLogin' | i18n" [subtitle]="'YubiKey'">
|
<bit-dialog dialogSize="large" [title]="'twoStepLogin' | i18n" [subtitle]="'YubiKey'">
|
||||||
<ng-container bitDialogContent>
|
<ng-container bitDialogContent>
|
||||||
<app-callout
|
<bit-callout
|
||||||
*ngIf="enabled"
|
*ngIf="enabled"
|
||||||
type="success"
|
type="success"
|
||||||
title="{{ 'enabled' | i18n }}"
|
title="{{ 'enabled' | i18n }}"
|
||||||
icon="bwi bwi-check-circle"
|
icon="bwi bwi-check-circle"
|
||||||
>
|
>
|
||||||
{{ "twoStepLoginProviderEnabled" | i18n }}
|
{{ "twoStepLoginProviderEnabled" | i18n }}
|
||||||
</app-callout>
|
</bit-callout>
|
||||||
<app-callout type="warning">
|
<bit-callout type="warning">
|
||||||
<p bitTypography="body1">{{ "twoFactorYubikeyWarning" | i18n }}</p>
|
<p bitTypography="body1">{{ "twoFactorYubikeyWarning" | i18n }}</p>
|
||||||
<ul class="tw-mb-0" bitTypography="body1">
|
<ul class="tw-mb-0" bitTypography="body1">
|
||||||
<li>{{ "twoFactorYubikeySupportUsb" | i18n }}</li>
|
<li>{{ "twoFactorYubikeySupportUsb" | i18n }}</li>
|
||||||
<li>{{ "twoFactorYubikeySupportMobile" | i18n }}</li>
|
<li>{{ "twoFactorYubikeySupportMobile" | i18n }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</app-callout>
|
</bit-callout>
|
||||||
<img class="tw-float-right mfaType3" alt="YubiKey OTP security key logo" />
|
<img class="tw-float-right mfaType3" alt="YubiKey OTP security key logo" />
|
||||||
<p bitTypography="body1">{{ "twoFactorYubikeyAdd" | i18n }}:</p>
|
<p bitTypography="body1">{{ "twoFactorYubikeyAdd" | i18n }}:</p>
|
||||||
<ol bitTypography="body1">
|
<ol bitTypography="body1">
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4" formArrayName="formKeys">
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4" formArrayName="formKeys">
|
||||||
<div class="tw-col-span-6" *ngFor="let k of keys; let i = index">
|
<div class="tw-col-span-6" *ngFor="let k of keys; let i = index">
|
||||||
<div [formGroupName]="i">
|
<div [formGroupName]="i">
|
||||||
<bit-label>{{ "yubikeyX" | i18n: i + 1 }}</bit-label>
|
<bit-label>{{ "yubikeyX" | i18n: (i + 1).toString() }}</bit-label>
|
||||||
<bit-form-field *ngIf="!keys[i].existingKey">
|
<bit-form-field *ngIf="!keys[i].existingKey">
|
||||||
<input bitInput type="password" formControlName="key" appInputVerbatim />
|
<input bitInput type="password" formControlName="key" appInputVerbatim />
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
import { CommonModule } from "@angular/common";
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, Inject, OnInit } from "@angular/core";
|
import { Component, Inject, OnInit } from "@angular/core";
|
||||||
import { FormArray, FormBuilder, FormControl, FormGroup } from "@angular/forms";
|
import {
|
||||||
|
FormArray,
|
||||||
|
FormBuilder,
|
||||||
|
FormControl,
|
||||||
|
FormGroup,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
} from "@angular/forms";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
@@ -12,7 +18,24 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { DIALOG_DATA, DialogConfig, DialogService, ToastService } from "@bitwarden/components";
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
CalloutModule,
|
||||||
|
CheckboxModule,
|
||||||
|
DIALOG_DATA,
|
||||||
|
DialogConfig,
|
||||||
|
DialogModule,
|
||||||
|
DialogRef,
|
||||||
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
IconButtonModule,
|
||||||
|
InputModule,
|
||||||
|
LinkModule,
|
||||||
|
ToastService,
|
||||||
|
TypographyModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component";
|
||||||
|
|
||||||
@@ -24,30 +47,49 @@ interface Key {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-setup-yubikey",
|
selector: "app-two-factor-setup-yubikey",
|
||||||
templateUrl: "two-factor-setup-yubikey.component.html",
|
templateUrl: "two-factor-setup-yubikey.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
FormFieldModule,
|
||||||
|
ButtonModule,
|
||||||
|
IconButtonModule,
|
||||||
|
CalloutModule,
|
||||||
|
CheckboxModule,
|
||||||
|
LinkModule,
|
||||||
|
TypographyModule,
|
||||||
|
InputModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
I18nPipe,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class TwoFactorSetupYubiKeyComponent
|
export class TwoFactorSetupYubiKeyComponent
|
||||||
extends TwoFactorSetupMethodBaseComponent
|
extends TwoFactorSetupMethodBaseComponent
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
type = TwoFactorProviderType.Yubikey;
|
type = TwoFactorProviderType.Yubikey;
|
||||||
keys: Key[];
|
keys: Key[] = [];
|
||||||
anyKeyHasNfc = false;
|
anyKeyHasNfc = false;
|
||||||
|
|
||||||
formPromise: Promise<TwoFactorYubiKeyResponse>;
|
formPromise: Promise<TwoFactorYubiKeyResponse> | undefined;
|
||||||
disablePromise: Promise<unknown>;
|
disablePromise: Promise<unknown> | undefined;
|
||||||
|
|
||||||
override componentName = "app-two-factor-yubikey";
|
override componentName = "app-two-factor-yubikey";
|
||||||
formGroup: FormGroup<{
|
formGroup:
|
||||||
formKeys: FormArray<FormControl<Key>>;
|
| FormGroup<{
|
||||||
anyKeyHasNfc: FormControl<boolean>;
|
formKeys: FormArray<FormControl<Key | null>>;
|
||||||
}>;
|
anyKeyHasNfc: FormControl<boolean | null>;
|
||||||
|
}>
|
||||||
|
| undefined;
|
||||||
|
|
||||||
get keysFormControl() {
|
get keysFormControl() {
|
||||||
return this.formGroup.controls.formKeys.controls;
|
return this.formGroup?.controls.formKeys.controls;
|
||||||
}
|
}
|
||||||
|
|
||||||
get anyKeyHasNfcFormControl() {
|
get anyKeyHasNfcFormControl() {
|
||||||
return this.formGroup.controls.anyKeyHasNfc;
|
return this.formGroup?.controls.anyKeyHasNfc;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -82,6 +124,9 @@ export class TwoFactorSetupYubiKeyComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshFormArrayData() {
|
refreshFormArrayData() {
|
||||||
|
if (!this.formGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formKeys = <FormArray>this.formGroup.get("formKeys");
|
const formKeys = <FormArray>this.formGroup.get("formKeys");
|
||||||
formKeys.clear();
|
formKeys.clear();
|
||||||
this.keys.forEach((val) => {
|
this.keys.forEach((val) => {
|
||||||
@@ -99,6 +144,9 @@ export class TwoFactorSetupYubiKeyComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
|
if (!this.formGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.formGroup.markAllAsTouched();
|
this.formGroup.markAllAsTouched();
|
||||||
if (this.formGroup.invalid) {
|
if (this.formGroup.invalid) {
|
||||||
return;
|
return;
|
||||||
@@ -117,14 +165,17 @@ export class TwoFactorSetupYubiKeyComponent
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected async enable() {
|
protected async enable() {
|
||||||
|
if (!this.formGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const keys = this.formGroup.controls.formKeys.value;
|
const keys = this.formGroup.controls.formKeys.value;
|
||||||
const request = await this.buildRequestModel(UpdateTwoFactorYubikeyOtpRequest);
|
const request = await this.buildRequestModel(UpdateTwoFactorYubikeyOtpRequest);
|
||||||
request.key1 = keys != null && keys.length > 0 ? keys[0].key : null;
|
request.key1 = keys != null && keys.length > 0 ? (keys[0]?.key ?? "") : "";
|
||||||
request.key2 = keys != null && keys.length > 1 ? keys[1].key : null;
|
request.key2 = keys != null && keys.length > 1 ? (keys[1]?.key ?? "") : "";
|
||||||
request.key3 = keys != null && keys.length > 2 ? keys[2].key : null;
|
request.key3 = keys != null && keys.length > 2 ? (keys[2]?.key ?? "") : "";
|
||||||
request.key4 = keys != null && keys.length > 3 ? keys[3].key : null;
|
request.key4 = keys != null && keys.length > 3 ? (keys[3]?.key ?? "") : "";
|
||||||
request.key5 = keys != null && keys.length > 4 ? keys[4].key : null;
|
request.key5 = keys != null && keys.length > 4 ? (keys[4]?.key ?? "") : "";
|
||||||
request.nfc = this.formGroup.value.anyKeyHasNfc;
|
request.nfc = this.formGroup.value.anyKeyHasNfc ?? false;
|
||||||
|
|
||||||
this.processResponse(await this.apiService.putTwoFactorYubiKey(request));
|
this.processResponse(await this.apiService.putTwoFactorYubiKey(request));
|
||||||
this.refreshFormArrayData();
|
this.refreshFormArrayData();
|
||||||
@@ -137,12 +188,16 @@ export class TwoFactorSetupYubiKeyComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
remove(pos: number) {
|
remove(pos: number) {
|
||||||
this.keys[pos].key = null;
|
this.keys[pos].key = "";
|
||||||
this.keys[pos].existingKey = null;
|
this.keys[pos].existingKey = "";
|
||||||
|
|
||||||
|
if (!this.keysFormControl || !this.keysFormControl[pos]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.keysFormControl[pos].setValue({
|
this.keysFormControl[pos].setValue({
|
||||||
existingKey: null,
|
existingKey: "",
|
||||||
key: null,
|
key: "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +228,9 @@ export class TwoFactorSetupYubiKeyComponent
|
|||||||
dialogService: DialogService,
|
dialogService: DialogService,
|
||||||
config: DialogConfig<AuthResponse<TwoFactorYubiKeyResponse>>,
|
config: DialogConfig<AuthResponse<TwoFactorYubiKeyResponse>>,
|
||||||
) {
|
) {
|
||||||
return dialogService.open<boolean>(TwoFactorSetupYubiKeyComponent, config);
|
return dialogService.open<boolean, AuthResponse<TwoFactorYubiKeyResponse>>(
|
||||||
|
TwoFactorSetupYubiKeyComponent,
|
||||||
|
config as DialogConfig<AuthResponse<TwoFactorYubiKeyResponse>, DialogRef<boolean>>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<app-header *ngIf="organizationId != null"></app-header>
|
<app-header *ngIf="organizationId != null"></app-header>
|
||||||
|
|
||||||
<bit-container>
|
<bit-container>
|
||||||
<div class="tabbed-header" *ngIf="organizationId == null">
|
<div class="tw-mt-6 tw-mb-2 tw-pb-2.5" *ngIf="organizationId == null">
|
||||||
<h1 *ngIf="!organizationId || !isEnterpriseOrg">{{ "twoStepLogin" | i18n }}</h1>
|
<h1 *ngIf="!organizationId || !isEnterpriseOrg">{{ "twoStepLogin" | i18n }}</h1>
|
||||||
<h1 *ngIf="organizationId && isEnterpriseOrg">{{ "twoStepLoginEnforcement" | i18n }}</h1>
|
<h1 *ngIf="organizationId && isEnterpriseOrg">{{ "twoStepLoginEnforcement" | i18n }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
{{ "providers" | i18n }}
|
{{ "providers" | i18n }}
|
||||||
<small *ngIf="loading">
|
<small *ngIf="loading">
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-spinner bwi-spin bwi-fw text-muted"
|
class="bwi bwi-spinner bwi-spin bwi-fw tw-text-muted"
|
||||||
title="{{ 'loading' | i18n }}"
|
title="{{ 'loading' | i18n }}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
@@ -45,32 +45,32 @@
|
|||||||
<bit-callout type="warning" *ngIf="showPolicyWarning">
|
<bit-callout type="warning" *ngIf="showPolicyWarning">
|
||||||
{{ "twoStepLoginPolicyUserWarning" | i18n }}
|
{{ "twoStepLoginPolicyUserWarning" | i18n }}
|
||||||
</bit-callout>
|
</bit-callout>
|
||||||
<ul class="list-group list-group-2fa">
|
<bit-item-group [attr.aria-label]="'providers' | i18n">
|
||||||
<li *ngFor="let p of providers" class="list-group-item d-flex align-items-center">
|
<bit-item *ngFor="let p of providers" class="tw-py-4">
|
||||||
<div class="logo-2fa d-flex justify-content-center">
|
<div slot="start" class="tw-min-w-[120px] tw-flex tw-justify-center">
|
||||||
<auth-two-factor-icon [provider]="p.type" [name]="p.name" />
|
<auth-two-factor-icon class="tw-flex tw-items-center" [provider]="p.type" [name]="p.name" />
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-4">
|
<div bit-item-content class="tw-px-4">
|
||||||
<h3 class="mb-0">
|
<h3 class="tw-mb-0">
|
||||||
<div
|
<div
|
||||||
class="font-weight-semibold tw-text-base"
|
class="tw-font-semibold tw-text-base"
|
||||||
[style]="p.enabled || p.premium ? 'display:inline-block' : ''"
|
[style]="p.enabled || p.premium ? 'display:inline-block' : ''"
|
||||||
>
|
>
|
||||||
{{ p.name }}
|
{{ p.name }}
|
||||||
</div>
|
</div>
|
||||||
<ng-container *ngIf="p.enabled">
|
<ng-container *ngIf="p.enabled">
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-check text-success bwi-fw"
|
class="bwi bwi-check tw-text-success-600 bwi-fw tw-ml-2"
|
||||||
title="{{ 'enabled' | i18n }}"
|
title="{{ 'enabled' | i18n }}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
<span class="tw-sr-only">{{ "enabled" | i18n }}</span>
|
<span class="tw-sr-only">{{ "enabled" | i18n }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<app-premium-badge *ngIf="p.premium"></app-premium-badge>
|
<app-premium-badge class="tw-ml-2" *ngIf="p.premium"></app-premium-badge>
|
||||||
</h3>
|
</h3>
|
||||||
{{ p.description }}
|
<div class="tw-mt-2 tw-text-wrap">{{ p.description }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-auto">
|
<bit-item-action slot="end">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
bitButton
|
bitButton
|
||||||
@@ -80,9 +80,9 @@
|
|||||||
>
|
>
|
||||||
{{ "manage" | i18n }}
|
{{ "manage" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</bit-item-action>
|
||||||
</li>
|
</bit-item>
|
||||||
</ul>
|
</bit-item-group>
|
||||||
</bit-container>
|
</bit-container>
|
||||||
|
|
||||||
<ng-template #duoTemplate></ng-template>
|
<ng-template #duoTemplate></ng-template>
|
||||||
|
|||||||
@@ -32,7 +32,10 @@ import { ProductTierType } from "@bitwarden/common/billing/enums";
|
|||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||||
import { DialogRef, DialogService } from "@bitwarden/components";
|
import { DialogRef, DialogService, ItemModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { LooseComponentsModule } from "../../../shared/loose-components.module";
|
||||||
|
import { SharedModule } from "../../../shared/shared.module";
|
||||||
|
|
||||||
import { TwoFactorRecoveryComponent } from "./two-factor-recovery.component";
|
import { TwoFactorRecoveryComponent } from "./two-factor-recovery.component";
|
||||||
import { TwoFactorSetupAuthenticatorComponent } from "./two-factor-setup-authenticator.component";
|
import { TwoFactorSetupAuthenticatorComponent } from "./two-factor-setup-authenticator.component";
|
||||||
@@ -45,6 +48,8 @@ import { TwoFactorVerifyComponent } from "./two-factor-verify.component";
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-setup",
|
selector: "app-two-factor-setup",
|
||||||
templateUrl: "two-factor-setup.component.html",
|
templateUrl: "two-factor-setup.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [ItemModule, LooseComponentsModule, SharedModule],
|
||||||
})
|
})
|
||||||
export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild("yubikeyTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("yubikeyTemplate", { read: ViewContainerRef, static: true })
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Component, EventEmitter, Inject, Output } from "@angular/core";
|
import { Component, EventEmitter, Inject, Output } from "@angular/core";
|
||||||
import { FormControl, FormGroup } from "@angular/forms";
|
import { FormControl, FormGroup, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
|
||||||
|
import { UserVerificationFormInputComponent } from "@bitwarden/auth/angular";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||||
@@ -13,7 +12,16 @@ import { TwoFactorResponse } from "@bitwarden/common/auth/types/two-factor-respo
|
|||||||
import { Verification } from "@bitwarden/common/auth/types/verification";
|
import { Verification } from "@bitwarden/common/auth/types/verification";
|
||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { DIALOG_DATA, DialogConfig, DialogRef, DialogService } from "@bitwarden/components";
|
import {
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
DIALOG_DATA,
|
||||||
|
DialogConfig,
|
||||||
|
DialogModule,
|
||||||
|
DialogRef,
|
||||||
|
DialogService,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
|
|
||||||
type TwoFactorVerifyDialogData = {
|
type TwoFactorVerifyDialogData = {
|
||||||
type: TwoFactorProviderType;
|
type: TwoFactorProviderType;
|
||||||
@@ -23,13 +31,22 @@ type TwoFactorVerifyDialogData = {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "app-two-factor-verify",
|
selector: "app-two-factor-verify",
|
||||||
templateUrl: "two-factor-verify.component.html",
|
templateUrl: "two-factor-verify.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
DialogModule,
|
||||||
|
I18nPipe,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
UserVerificationFormInputComponent,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class TwoFactorVerifyComponent {
|
export class TwoFactorVerifyComponent {
|
||||||
type: TwoFactorProviderType;
|
type: TwoFactorProviderType;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
@Output() onAuthed = new EventEmitter<AuthResponse<TwoFactorResponse>>();
|
@Output() onAuthed = new EventEmitter<AuthResponse<TwoFactorResponse>>();
|
||||||
|
|
||||||
formPromise: Promise<TwoFactorResponse>;
|
formPromise: Promise<TwoFactorResponse> | undefined;
|
||||||
|
|
||||||
protected formGroup = new FormGroup({
|
protected formGroup = new FormGroup({
|
||||||
secret: new FormControl<Verification | null>(null),
|
secret: new FormControl<Verification | null>(null),
|
||||||
@@ -49,22 +66,25 @@ export class TwoFactorVerifyComponent {
|
|||||||
|
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
try {
|
try {
|
||||||
let hashedSecret: string;
|
let hashedSecret = "";
|
||||||
this.formPromise = this.userVerificationService
|
if (!this.formGroup.value.secret) {
|
||||||
.buildRequest(this.formGroup.value.secret)
|
throw new Error("Secret is required");
|
||||||
.then((request) => {
|
}
|
||||||
hashedSecret =
|
|
||||||
this.formGroup.value.secret.type === VerificationType.MasterPassword
|
const secret = this.formGroup.value.secret!;
|
||||||
? request.masterPasswordHash
|
this.formPromise = this.userVerificationService.buildRequest(secret).then((request) => {
|
||||||
: request.otp;
|
hashedSecret =
|
||||||
return this.apiCall(request);
|
secret.type === VerificationType.MasterPassword
|
||||||
});
|
? request.masterPasswordHash
|
||||||
|
: request.otp;
|
||||||
|
return this.apiCall(request);
|
||||||
|
});
|
||||||
|
|
||||||
const response = await this.formPromise;
|
const response = await this.formPromise;
|
||||||
this.dialogRef.close({
|
this.dialogRef.close({
|
||||||
response: response,
|
response: response,
|
||||||
secret: hashedSecret,
|
secret: hashedSecret,
|
||||||
verificationType: this.formGroup.value.secret.type,
|
verificationType: secret.type,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ErrorResponse && e.statusCode === 400) {
|
if (e instanceof ErrorResponse && e.statusCode === 400) {
|
||||||
@@ -88,6 +108,8 @@ export class TwoFactorVerifyComponent {
|
|||||||
return this.i18nService.t("authenticatorAppTitle");
|
return this.i18nService.t("authenticatorAppTitle");
|
||||||
case TwoFactorProviderType.Yubikey:
|
case TwoFactorProviderType.Yubikey:
|
||||||
return "Yubikey";
|
return "Yubikey";
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown two-factor type: ${this.type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +132,15 @@ export class TwoFactorVerifyComponent {
|
|||||||
return this.apiService.getTwoFactorAuthenticator(request);
|
return this.apiService.getTwoFactorAuthenticator(request);
|
||||||
case TwoFactorProviderType.Yubikey:
|
case TwoFactorProviderType.Yubikey:
|
||||||
return this.apiService.getTwoFactorYubiKey(request);
|
return this.apiService.getTwoFactorYubiKey(request);
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown two-factor type: ${this.type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static open(dialogService: DialogService, config: DialogConfig<TwoFactorVerifyDialogData>) {
|
static open(dialogService: DialogService, config: DialogConfig<TwoFactorVerifyDialogData>) {
|
||||||
return dialogService.open<AuthResponse<any>>(TwoFactorVerifyComponent, config);
|
return dialogService.open<AuthResponse<any>, TwoFactorVerifyDialogData>(
|
||||||
|
TwoFactorVerifyComponent,
|
||||||
|
config as DialogConfig<TwoFactorVerifyDialogData, DialogRef<AuthResponse<any>>>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,14 +33,6 @@ import { ApiKeyComponent } from "../auth/settings/security/api-key.component";
|
|||||||
import { ChangeKdfModule } from "../auth/settings/security/change-kdf/change-kdf.module";
|
import { ChangeKdfModule } from "../auth/settings/security/change-kdf/change-kdf.module";
|
||||||
import { SecurityKeysComponent } from "../auth/settings/security/security-keys.component";
|
import { SecurityKeysComponent } from "../auth/settings/security/security-keys.component";
|
||||||
import { SecurityComponent } from "../auth/settings/security/security.component";
|
import { SecurityComponent } from "../auth/settings/security/security.component";
|
||||||
import { TwoFactorRecoveryComponent } from "../auth/settings/two-factor/two-factor-recovery.component";
|
|
||||||
import { TwoFactorSetupAuthenticatorComponent } from "../auth/settings/two-factor/two-factor-setup-authenticator.component";
|
|
||||||
import { TwoFactorSetupDuoComponent } from "../auth/settings/two-factor/two-factor-setup-duo.component";
|
|
||||||
import { TwoFactorSetupEmailComponent } from "../auth/settings/two-factor/two-factor-setup-email.component";
|
|
||||||
import { TwoFactorSetupWebAuthnComponent } from "../auth/settings/two-factor/two-factor-setup-webauthn.component";
|
|
||||||
import { TwoFactorSetupYubiKeyComponent } from "../auth/settings/two-factor/two-factor-setup-yubikey.component";
|
|
||||||
import { TwoFactorSetupComponent } from "../auth/settings/two-factor/two-factor-setup.component";
|
|
||||||
import { TwoFactorVerifyComponent } from "../auth/settings/two-factor/two-factor-verify.component";
|
|
||||||
import { UserVerificationModule } from "../auth/shared/components/user-verification";
|
import { UserVerificationModule } from "../auth/shared/components/user-verification";
|
||||||
import { UpdatePasswordComponent } from "../auth/update-password.component";
|
import { UpdatePasswordComponent } from "../auth/update-password.component";
|
||||||
import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component";
|
import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component";
|
||||||
@@ -145,14 +137,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
SetPasswordComponent,
|
SetPasswordComponent,
|
||||||
SponsoredFamiliesComponent,
|
SponsoredFamiliesComponent,
|
||||||
SponsoringOrgRowComponent,
|
SponsoringOrgRowComponent,
|
||||||
TwoFactorSetupAuthenticatorComponent,
|
|
||||||
TwoFactorSetupDuoComponent,
|
|
||||||
TwoFactorSetupEmailComponent,
|
|
||||||
TwoFactorRecoveryComponent,
|
|
||||||
TwoFactorSetupComponent,
|
|
||||||
TwoFactorVerifyComponent,
|
|
||||||
TwoFactorSetupWebAuthnComponent,
|
|
||||||
TwoFactorSetupYubiKeyComponent,
|
|
||||||
UpdatePasswordComponent,
|
UpdatePasswordComponent,
|
||||||
UpdateTempPasswordComponent,
|
UpdateTempPasswordComponent,
|
||||||
VerifyEmailTokenComponent,
|
VerifyEmailTokenComponent,
|
||||||
@@ -204,13 +188,6 @@ import { SharedModule } from "./shared.module";
|
|||||||
SetPasswordComponent,
|
SetPasswordComponent,
|
||||||
SponsoredFamiliesComponent,
|
SponsoredFamiliesComponent,
|
||||||
SponsoringOrgRowComponent,
|
SponsoringOrgRowComponent,
|
||||||
TwoFactorSetupAuthenticatorComponent,
|
|
||||||
TwoFactorSetupDuoComponent,
|
|
||||||
TwoFactorSetupEmailComponent,
|
|
||||||
TwoFactorSetupComponent,
|
|
||||||
TwoFactorVerifyComponent,
|
|
||||||
TwoFactorSetupWebAuthnComponent,
|
|
||||||
TwoFactorSetupYubiKeyComponent,
|
|
||||||
UpdateTempPasswordComponent,
|
UpdateTempPasswordComponent,
|
||||||
UpdatePasswordComponent,
|
UpdatePasswordComponent,
|
||||||
UserLayoutComponent,
|
UserLayoutComponent,
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -30,12 +30,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group-2fa {
|
|
||||||
.logo-2fa {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@each $mfaType in $mfaTypes {
|
@each $mfaType in $mfaTypes {
|
||||||
.mfaType#{$mfaType} {
|
.mfaType#{$mfaType} {
|
||||||
content: url("../images/two-factor/" + $mfaType + ".png");
|
content: url("../images/two-factor/" + $mfaType + ".png");
|
||||||
@@ -66,14 +60,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.recovery-code-img {
|
|
||||||
@include themify($themes) {
|
|
||||||
content: url("../images/two-factor/rc" + themed("mfaLogoSuffix"));
|
|
||||||
max-width: 100px;
|
|
||||||
max-height: 45px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
@include themify($themes) {
|
@include themify($themes) {
|
||||||
background-color: themed("pwStrengthBackground");
|
background-color: themed("pwStrengthBackground");
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe("Menu", () => {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "test-app",
|
selector: "test-app",
|
||||||
template: `
|
template: `
|
||||||
<button type="button" [bitMenuTriggerFor]="testMenu" class="testclass">Open menu</button>
|
<button type="button" [bitMenuTriggerFor]="testMenu">Open menu</button>
|
||||||
|
|
||||||
<bit-menu #testMenu>
|
<bit-menu #testMenu>
|
||||||
<a id="item1" bitMenuItem>Item 1</a>
|
<a id="item1" bitMenuItem>Item 1</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user