diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts
index fc8356505f5..4d8deb5b73f 100644
--- a/apps/web/src/app/oss-routing.module.ts
+++ b/apps/web/src/app/oss-routing.module.ts
@@ -39,6 +39,7 @@ import {
TwoFactorAuthGuard,
NewDeviceVerificationComponent,
DeviceVerificationIcon,
+ ChangePasswordComponent,
} from "@bitwarden/auth/angular";
import { LockComponent } from "@bitwarden/key-management-ui";
import {
@@ -63,8 +64,8 @@ import { AccountComponent } from "./auth/settings/account/account.component";
import { EmergencyAccessComponent } from "./auth/settings/emergency-access/emergency-access.component";
import { EmergencyAccessViewComponent } from "./auth/settings/emergency-access/view/emergency-access-view.component";
import { SecurityRoutingModule } from "./auth/settings/security/security-routing.module";
-import { UpdatePasswordComponent } from "./auth/update-password.component";
-import { UpdateTempPasswordComponent } from "./auth/update-temp-password.component";
+// import { UpdatePasswordComponent } from "./auth/update-password.component";
+// import { UpdateTempPasswordComponent } from "./auth/update-temp-password.component";
import { VerifyEmailTokenComponent } from "./auth/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "./auth/verify-recover-delete.component";
import { SponsoredFamiliesComponent } from "./billing/settings/sponsored-families.component";
@@ -145,17 +146,62 @@ const routes: Routes = [
data: { titleId: "deleteOrganization" },
},
{
- path: "update-temp-password",
- component: UpdateTempPasswordComponent,
- canActivate: [authGuard],
- data: { titleId: "updateTempPassword" } satisfies RouteDataProperties,
+ path: "",
+ component: AnonLayoutWrapperComponent,
+ children: [
+ {
+ path: "update-temp-password",
+ children: [
+ {
+ path: "",
+ component: ChangePasswordComponent,
+ },
+ ],
+ data: {
+ pageIcon: undefined,
+ hideFooter: true,
+ } satisfies AnonLayoutWrapperData,
+ },
+ ],
},
{
- path: "update-password",
- component: UpdatePasswordComponent,
- canActivate: [authGuard],
+ component: AnonLayoutWrapperComponent,
+ path: "",
+ children: [
+ {
+ path: "update-password",
+ canActivate: [authGuard],
+ children: [
+ {
+ path: "",
+ component: ChangePasswordComponent,
+ },
+ ],
+ data: {
+ pageIcon: undefined,
+ hideFooter: true,
+ } satisfies AnonLayoutWrapperData,
+ },
+ ],
data: { titleId: "updatePassword" } satisfies RouteDataProperties,
},
+ // {
+ // path: "update-temp-password",
+ // component: ChangePasswordComponent,
+ // data: {}
+ // },
+ // {
+ // path: "update-temp-password",
+ // component: UpdateTempPasswordComponent,
+ // canActivate: [authGuard],
+ // data: { titleId: "updateTempPassword" } satisfies RouteDataProperties,
+ // },
+ // {
+ // path: "update-password",
+ // component: UpdatePasswordComponent,
+ // canActivate: [authGuard],
+ // data: { titleId: "updatePassword" } satisfies RouteDataProperties,
+ // },
{
path: "migrate-legacy-encryption",
loadComponent: () =>
diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html
index 95b1e6cadfe..1ade9ba06a6 100644
--- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html
+++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.html
@@ -5,6 +5,7 @@
[showReadonlyHostname]="showReadonlyHostname"
[maxWidth]="maxWidth"
[titleAreaMaxWidth]="titleAreaMaxWidth"
+ [hideFooter]="hideFooter"
>
diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts
index 04dc3b6dfd2..c0b072fd8f1 100644
--- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts
+++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.component.ts
@@ -39,6 +39,11 @@ export interface AnonLayoutWrapperData {
* Optional flag to set the max-width of the title area. Defaults to null if not provided.
*/
titleAreaMaxWidth?: "md";
+
+ /**
+ * Optional flag to hide the whole footer.
+ */
+ hideFooter?: boolean;
}
@Component({
@@ -55,6 +60,7 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
protected showReadonlyHostname: boolean;
protected maxWidth: "md" | "3xl";
protected titleAreaMaxWidth: "md";
+ protected hideFooter: boolean;
constructor(
private router: Router,
@@ -106,6 +112,7 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
this.showReadonlyHostname = Boolean(firstChildRouteData["showReadonlyHostname"]);
this.maxWidth = firstChildRouteData["maxWidth"];
this.titleAreaMaxWidth = firstChildRouteData["titleAreaMaxWidth"];
+ this.hideFooter = Boolean(firstChildRouteData["hideFooter"]);
}
private listenForServiceDataChanges() {
diff --git a/libs/auth/src/angular/change-password/change-password.component.html b/libs/auth/src/angular/change-password/change-password.component.html
index 36f3d1cf9ba..c6c2054f750 100644
--- a/libs/auth/src/angular/change-password/change-password.component.html
+++ b/libs/auth/src/angular/change-password/change-password.component.html
@@ -9,11 +9,13 @@
}
diff --git a/libs/auth/src/angular/change-password/change-password.component.ts b/libs/auth/src/angular/change-password/change-password.component.ts
index 43a9e0d89ce..a7b0509107c 100644
--- a/libs/auth/src/angular/change-password/change-password.component.ts
+++ b/libs/auth/src/angular/change-password/change-password.component.ts
@@ -1,10 +1,12 @@
import { Component, Input, OnInit } from "@angular/core";
+import { Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
+import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
@@ -15,9 +17,13 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SyncService } from "@bitwarden/common/platform/sync";
import { UserId } from "@bitwarden/common/types/guid";
import { UserKey } from "@bitwarden/common/types/key";
-import { ToastService } from "@bitwarden/components";
+import { DialogService, ToastService, Translation } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
import { I18nPipe } from "@bitwarden/ui-common";
+// import {
+// AcceptOrganizationInviteService
+// } from "@bitwarden/web-vault/src/app/auth/organization-invite/accept-organization.service";
+// import { RouterService } from "@bitwarden/web-vault/src/app/core";
import {
InputPasswordComponent,
@@ -38,11 +44,13 @@ export class ChangePasswordComponent implements OnInit {
activeAccount: Account | null = null;
email?: string;
- userId?: UserId;
+ activeUserId?: UserId;
masterPasswordPolicyOptions?: MasterPasswordPolicyOptions;
initializing = true;
userkeyRotationV2 = false;
formPromise?: Promise;
+ secondaryButtonText?: Translation = undefined;
+ forceSetPasswordReason: ForceSetPasswordReason = ForceSetPasswordReason.None;
constructor(
private accountService: AccountService,
@@ -56,26 +64,75 @@ export class ChangePasswordComponent implements OnInit {
private policyService: PolicyService,
private toastService: ToastService,
private syncService: SyncService,
+ // private routerService: RouterService,
+ // private acceptOrganizationInviteService: AcceptOrganizationInviteService,
+ private dialogService: DialogService,
+ private router: Router,
) {}
async ngOnInit() {
this.userkeyRotationV2 = await this.configService.getFeatureFlag(FeatureFlag.UserKeyRotationV2);
this.activeAccount = await firstValueFrom(this.accountService.activeAccount$);
- this.userId = this.activeAccount?.id;
+ this.activeUserId = this.activeAccount?.id;
this.email = this.activeAccount?.email;
- if (!this.userId) {
+ if (!this.activeUserId) {
throw new Error("userId not found");
}
this.masterPasswordPolicyOptions = await firstValueFrom(
- this.policyService.masterPasswordPolicyOptions$(this.userId),
+ this.policyService.masterPasswordPolicyOptions$(this.activeUserId),
);
+ this.forceSetPasswordReason = await firstValueFrom(
+ this.masterPasswordService.forceSetPasswordReason$(this.activeUserId),
+ );
+
+ if (
+ this.forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset ||
+ this.forceSetPasswordReason === ForceSetPasswordReason.WeakMasterPassword
+ ) {
+ this.secondaryButtonText = { key: "cancel" };
+ } else {
+ this.secondaryButtonText = undefined;
+ }
+
this.initializing = false;
}
+ async performSecondaryAction() {
+ if (
+ this.forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset ||
+ this.forceSetPasswordReason === ForceSetPasswordReason.WeakMasterPassword
+ ) {
+ await this.logOut();
+ } else {
+ await this.cancel();
+ }
+ }
+
+ async logOut() {
+ const confirmed = await this.dialogService.openSimpleDialog({
+ title: { key: "logOut" },
+ content: { key: "logOutConfirmation" },
+ acceptButtonText: { key: "logOut" },
+ type: "warning",
+ });
+
+ if (confirmed) {
+ this.messagingService.send("logout");
+ }
+ }
+
+ async cancel() {
+ // clearing the login redirect url so that the user
+ // does not join the organization if they cancel
+ // await this.routerService.getAndClearLoginRedirectUrl();
+ // await this.acceptOrganizationInviteService.clearOrganizationInvitation();
+ // await this.router.navigate(["/vault"]);
+ }
+
async handlePasswordFormSubmit(passwordInputResult: PasswordInputResult) {
if (this.userkeyRotationV2) {
await this.submitNew(passwordInputResult);
@@ -104,11 +161,11 @@ export class ChangePasswordComponent implements OnInit {
passwordInputResult.newPasswordHint,
);
} else {
- if (!this.userId) {
+ if (!this.activeUserId) {
throw new Error("userId not found");
}
- await this.changePasswordService.changePassword(passwordInputResult, this.userId);
+ await this.changePasswordService.changePassword(passwordInputResult, this.activeUserId);
this.toastService.showToast({
variant: "success",
@@ -128,7 +185,7 @@ export class ChangePasswordComponent implements OnInit {
}
private async submitOld(passwordInputResult: PasswordInputResult) {
- if (!this.userId) {
+ if (!this.activeUserId) {
throw new Error("userId not found");
}
@@ -165,11 +222,11 @@ export class ChangePasswordComponent implements OnInit {
// we need to save this for local masterkey verification during rotation
await this.masterPasswordService.setMasterKeyHash(
passwordInputResult.newLocalMasterKeyHash,
- this.userId as UserId,
+ this.activeUserId as UserId,
);
await this.masterPasswordService.setMasterKey(
passwordInputResult.newMasterKey,
- this.userId as UserId,
+ this.activeUserId as UserId,
);
return this.updateKey(passwordInputResult.newPassword);
});