diff --git a/apps/browser/src/popup/images/two-factor/rc-w.png b/apps/browser/src/popup/images/two-factor/rc-w.png deleted file mode 100644 index e83b8db132..0000000000 Binary files a/apps/browser/src/popup/images/two-factor/rc-w.png and /dev/null differ diff --git a/apps/browser/src/popup/images/two-factor/rc.png b/apps/browser/src/popup/images/two-factor/rc.png deleted file mode 100644 index 4bebdf936c..0000000000 Binary files a/apps/browser/src/popup/images/two-factor/rc.png and /dev/null differ diff --git a/apps/browser/src/popup/scss/plugins.scss b/apps/browser/src/popup/scss/plugins.scss index b8ac8697b7..591e8a1bd0 100644 --- a/apps/browser/src/popup/scss/plugins.scss +++ b/apps/browser/src/popup/scss/plugins.scss @@ -21,11 +21,3 @@ max-width: 100px; } } - -.recovery-code-img { - @include themify($themes) { - content: url("../images/two-factor/rc" + themed("mfaLogoSuffix")); - max-width: 100px; - max-height: 45px; - } -} diff --git a/apps/desktop/src/images/two-factor/rc-w.png b/apps/desktop/src/images/two-factor/rc-w.png deleted file mode 100644 index e83b8db132..0000000000 Binary files a/apps/desktop/src/images/two-factor/rc-w.png and /dev/null differ diff --git a/apps/desktop/src/images/two-factor/rc.png b/apps/desktop/src/images/two-factor/rc.png deleted file mode 100644 index 4bebdf936c..0000000000 Binary files a/apps/desktop/src/images/two-factor/rc.png and /dev/null differ diff --git a/apps/desktop/src/scss/plugins.scss b/apps/desktop/src/scss/plugins.scss index b8ac8697b7..591e8a1bd0 100644 --- a/apps/desktop/src/scss/plugins.scss +++ b/apps/desktop/src/scss/plugins.scss @@ -21,11 +21,3 @@ max-width: 100px; } } - -.recovery-code-img { - @include themify($themes) { - content: url("../images/two-factor/rc" + themed("mfaLogoSuffix")); - max-width: 100px; - max-height: 45px; - } -} diff --git a/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts b/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts index 2a2068d581..bfff3b2aa2 100644 --- a/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts +++ b/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts @@ -1,5 +1,7 @@ import { NgModule } from "@angular/core"; +import { ItemModule } from "@bitwarden/components"; + import { LooseComponentsModule, SharedModule } from "../../../shared"; import { AccountFingerprintComponent } from "../../../shared/components/account-fingerprint/account-fingerprint.component"; import { PoliciesModule } from "../../organizations/policies"; @@ -15,6 +17,7 @@ import { TwoFactorSetupComponent } from "./two-factor-setup.component"; PoliciesModule, OrganizationSettingsRoutingModule, AccountFingerprintComponent, + ItemModule, ], declarations: [AccountComponent, TwoFactorSetupComponent], }) diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts index 19e01af4b0..75a9766131 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-recovery.component.ts @@ -1,36 +1,50 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore +import { CommonModule } from "@angular/common"; import { Component, Inject } from "@angular/core"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { TwoFactorRecoverResponse } from "@bitwarden/common/auth/models/response/two-factor-recover.response"; 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({ selector: "app-two-factor-recovery", templateUrl: "two-factor-recovery.component.html", + standalone: true, + imports: [CommonModule, DialogModule, ButtonModule, TypographyModule, I18nPipe], }) export class TwoFactorRecoveryComponent { type = -1; - code: string; - authed: boolean; + code: string = ""; + authed: boolean = false; twoFactorProviderType = TwoFactorProviderType; constructor( - @Inject(DIALOG_DATA) protected data: any, + @Inject(DIALOG_DATA) protected data: { response: { response: TwoFactorRecoverResponse } }, private i18nService: I18nService, ) { this.auth(data.response); } - auth(authResponse: any) { + auth(authResponse: { response: TwoFactorRecoverResponse }) { this.authed = true; this.processResponse(authResponse.response); } print() { const w = window.open(); + if (!w) { + // return early if the window is not open + return; + } w.document.write( '
' + "

" + @@ -47,9 +61,9 @@ export class TwoFactorRecoveryComponent { w.print(); } - private formatString(s: string) { + private formatString(s: string): string { if (s == null) { - return null; + return ""; } return s .replace(/(.{4})/g, "$1 ") @@ -61,7 +75,13 @@ export class TwoFactorRecoveryComponent { this.code = this.formatString(response.code); } - static open(dialogService: DialogService, config: DialogConfig) { + static open( + dialogService: DialogService, + config: DialogConfig< + { response: { response: TwoFactorRecoverResponse } }, + DialogRef + >, + ) { return dialogService.open(TwoFactorRecoveryComponent, config); } } diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.html b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.html index a31d4c3345..1595c0350d 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.html +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-authenticator.component.html @@ -41,7 +41,7 @@ {{ "twoStepAuthenticatorInstructionSuffix" | i18n }}

-

+

- + {{ "twoStepLoginProviderEnabled" | i18n }} - + Duo logo {{ "twoFactorDuoClientId" | i18n }}: {{ clientId }}
diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts index 833ce5c1cb..bada3301a9 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-duo.component.ts @@ -1,7 +1,6 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore +import { CommonModule } from "@angular/common"; 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 { 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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { + AsyncActionsModule, + ButtonModule, + CalloutModule, DIALOG_DATA, DialogConfig, + DialogModule, DialogRef, DialogService, + FormFieldModule, + IconModule, + InputModule, ToastService, + TypographyModule, } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component"; @Component({ selector: "app-two-factor-setup-duo", templateUrl: "two-factor-setup-duo.component.html", + standalone: true, + imports: [ + CommonModule, + DialogModule, + FormFieldModule, + InputModule, + TypographyModule, + ButtonModule, + IconModule, + I18nPipe, + ReactiveFormsModule, + AsyncActionsModule, + CalloutModule, + ], }) export class TwoFactorSetupDuoComponent extends TwoFactorSetupMethodBaseComponent @@ -63,23 +85,23 @@ export class TwoFactorSetupDuoComponent ); } - get clientId() { - return this.formGroup.get("clientId").value; + get clientId(): string { + return this.formGroup.get("clientId")?.value || ""; } - get clientSecret() { - return this.formGroup.get("clientSecret").value; + get clientSecret(): string { + return this.formGroup.get("clientSecret")?.value || ""; } - get host() { - return this.formGroup.get("host").value; + get host(): string { + return this.formGroup.get("host")?.value || ""; } set clientId(value: string) { - this.formGroup.get("clientId").setValue(value); + this.formGroup.get("clientId")?.setValue(value); } set clientSecret(value: string) { - this.formGroup.get("clientSecret").setValue(value); + this.formGroup.get("clientSecret")?.setValue(value); } set host(value: string) { - this.formGroup.get("host").setValue(value); + this.formGroup.get("host")?.setValue(value); } async ngOnInit() { @@ -147,7 +169,10 @@ export class TwoFactorSetupDuoComponent dialogService: DialogService, config: DialogConfig, ) => { - return dialogService.open(TwoFactorSetupDuoComponent, config); + return dialogService.open( + TwoFactorSetupDuoComponent, + config as DialogConfig>, + ); }; } diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts index 40f50a3393..c5692c3f08 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-email.component.ts @@ -1,7 +1,6 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore +import { CommonModule } from "@angular/common"; 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 { 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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { + AsyncActionsModule, + ButtonModule, + CalloutModule, DIALOG_DATA, DialogConfig, + DialogModule, DialogRef, DialogService, + FormFieldModule, + IconModule, + InputModule, ToastService, + TypographyModule, } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; import { TwoFactorSetupMethodBaseComponent } from "./two-factor-setup-method-base.component"; @Component({ selector: "app-two-factor-setup-email", 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 extends TwoFactorSetupMethodBaseComponent @@ -36,8 +57,8 @@ export class TwoFactorSetupEmailComponent { @Output() onChangeStatus: EventEmitter = new EventEmitter(); type = TwoFactorProviderType.Email; - sentEmail: string; - emailPromise: Promise; + sentEmail: string = ""; + emailPromise: Promise | undefined; override componentName = "app-two-factor-email"; formGroup = this.formBuilder.group({ token: ["", [Validators.required]], @@ -67,17 +88,17 @@ export class TwoFactorSetupEmailComponent toastService, ); } - get token() { - return this.formGroup.get("token").value; + get token(): string { + return this.formGroup.get("token")?.value || ""; } - set token(value: string) { - this.formGroup.get("token").setValue(value); + set token(value: string | null) { + this.formGroup.get("token")?.setValue(value || ""); } - get email() { - return this.formGroup.get("email").value; + get email(): string { + return this.formGroup.get("email")?.value || ""; } - set email(value: string) { - this.formGroup.get("email").setValue(value); + set email(value: string | null | undefined) { + this.formGroup.get("email")?.setValue(value || ""); } async ngOnInit() { @@ -149,6 +170,9 @@ export class TwoFactorSetupEmailComponent dialogService: DialogService, config: DialogConfig>, ) { - return dialogService.open(TwoFactorSetupEmailComponent, config); + return dialogService.open>( + TwoFactorSetupEmailComponent, + config as DialogConfig, DialogRef>, + ); } } diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts index b87b92a965..0654ad126e 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-method-base.component.ts @@ -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 { 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). */ -@Directive() +@Directive({ + standalone: true, +}) export abstract class TwoFactorSetupMethodBaseComponent { @Output() onUpdated = new EventEmitter(); - type: TwoFactorProviderType; - organizationId: string; + type: TwoFactorProviderType | undefined; + organizationId: string | null = null; twoFactorProviderType = TwoFactorProviderType; enabled = false; authed = false; - protected hashedSecret: string; - protected verificationType: VerificationType; + protected hashedSecret: string | undefined; + protected verificationType: VerificationType | undefined; protected componentName = ""; constructor( @@ -74,6 +74,9 @@ export abstract class TwoFactorSetupMethodBaseComponent { try { const request = await this.buildRequestModel(TwoFactorProviderRequest); + if (this.type === undefined) { + throw new Error("Two-factor provider type is required"); + } request.type = this.type; if (this.organizationId != null) { promise = this.apiService.putTwoFactorOrganizationDisable(this.organizationId, request); @@ -84,7 +87,7 @@ export abstract class TwoFactorSetupMethodBaseComponent { this.enabled = false; this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("twoStepDisabled"), }); this.onUpdated.emit(false); @@ -105,6 +108,9 @@ export abstract class TwoFactorSetupMethodBaseComponent { } const request = await this.buildRequestModel(TwoFactorProviderRequest); + if (this.type === undefined) { + throw new Error("Two-factor provider type is required"); + } request.type = this.type; if (this.organizationId != null) { await this.apiService.putTwoFactorOrganizationDisable(this.organizationId, request); @@ -114,7 +120,7 @@ export abstract class TwoFactorSetupMethodBaseComponent { this.enabled = false; this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("twoStepDisabled"), }); this.onUpdated.emit(false); @@ -123,6 +129,9 @@ export abstract class TwoFactorSetupMethodBaseComponent { protected async buildRequestModel( requestClass: new () => T, ) { + if (this.hashedSecret === undefined || this.verificationType === undefined) { + throw new Error("User verification data is missing"); + } return this.userVerificationService.buildRequest( { secret: this.hashedSecret, diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.html b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.html index 4d50516163..767934cf3d 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.html +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-webauthn.component.html @@ -5,23 +5,23 @@ [subtitle]="'webAuthnTitle' | i18n" > - {{ "twoStepLoginProviderEnabled" | i18n }} - - + +

{{ "twoFactorWebAuthnWarning1" | i18n }}

- + FIDO2 WebAuthn logo