diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts
index 7a629458eec..6bcaed12d3c 100644
--- a/apps/web/src/app/oss-routing.module.ts
+++ b/apps/web/src/app/oss-routing.module.ts
@@ -148,7 +148,7 @@ const routes: Routes = [
canActivate: [
canAccessFeature(
FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
- true,
+ false,
"change-password",
false,
),
@@ -600,15 +600,7 @@ const routes: Routes = [
},
{
path: "change-password",
- data: {
- hideFooter: true,
- } satisfies AnonLayoutWrapperData,
- children: [
- {
- path: "",
- component: ChangePasswordComponent,
- },
- ],
+ component: ChangePasswordComponent,
},
],
},
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 1ade9ba06a6..95b1e6cadfe 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,7 +5,6 @@
[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 5ae2fdbf347..f331c3ac3d1 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,11 +39,6 @@ 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({
@@ -60,7 +55,6 @@ export class AnonLayoutWrapperComponent implements OnInit, OnDestroy {
protected showReadonlyHostname: boolean;
protected maxWidth: "md" | "3xl";
protected titleAreaMaxWidth: "md";
- protected hideFooter: boolean;
constructor(
private router: Router,
@@ -112,7 +106,6 @@ 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 eee0c4fe2ea..f6ba86ef7c2 100644
--- a/libs/auth/src/angular/change-password/change-password.component.html
+++ b/libs/auth/src/angular/change-password/change-password.component.html
@@ -10,7 +10,6 @@
[flow]="inputPasswordFlow"
[email]="email"
[userId]="activeUserId"
- [forceSetPasswordReason]="forceSetPasswordReason"
[loading]="submitting"
[masterPasswordPolicyOptions]="masterPasswordPolicyOptions"
[inlineButtons]="true"
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 c8309098a91..85d38d1c048 100644
--- a/libs/auth/src/angular/change-password/change-password.component.ts
+++ b/libs/auth/src/angular/change-password/change-password.component.ts
@@ -11,7 +11,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { UserId } from "@bitwarden/common/types/guid";
-import { DialogService, ToastService, Translation } from "@bitwarden/components";
+import { DialogService, ToastService } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
import { AnonLayoutWrapperDataService } from "../anon-layout/anon-layout-wrapper-data.service";
@@ -24,6 +24,14 @@ import { PasswordInputResult } from "../input-password/password-input-result";
import { ChangePasswordService } from "./change-password.service.abstraction";
+/**
+ * Change Password Component
+ *
+ * NOTE: The change password component uses the input-password component which will show the
+ * current password input form in some flows, although it could be left off. This is intentional
+ * and by design to maintain a strong security posture as some flows could have the user
+ * end up at a change password without having one before.
+ */
@Component({
standalone: true,
selector: "auth-change-password",
@@ -41,7 +49,6 @@ export class ChangePasswordComponent implements OnInit {
submitting = false;
formPromise?: Promise;
forceSetPasswordReason: ForceSetPasswordReason = ForceSetPasswordReason.None;
- warningText?: Translation;
constructor(
private accountService: AccountService,
@@ -59,11 +66,16 @@ export class ChangePasswordComponent implements OnInit {
async ngOnInit() {
this.activeAccount = await firstValueFrom(this.accountService.activeAccount$);
- this.activeUserId = this.activeAccount?.id;
- this.email = this.activeAccount?.email;
+
+ if (!this.activeAccount) {
+ throw new Error("No active active account found while trying to change passwords.");
+ }
+
+ this.activeUserId = this.activeAccount.id;
+ this.email = this.activeAccount.email;
if (!this.activeUserId) {
- throw new Error("userId not found");
+ throw new Error("activeUserId not found");
}
this.masterPasswordPolicyOptions = await firstValueFrom(
@@ -76,10 +88,6 @@ export class ChangePasswordComponent implements OnInit {
this.initializing = false;
- if (this.masterPasswordPolicyOptions?.enforceOnLogin) {
- this.warningText = { key: "masterPasswordInvalidWarning" };
- }
-
if (this.forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset) {
this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
pageIcon: LockIcon,
@@ -91,7 +99,6 @@ export class ChangePasswordComponent implements OnInit {
pageIcon: LockIcon,
pageTitle: { key: "updateMasterPassword" },
pageSubtitle: { key: "updateMasterPasswordSubtitle" },
- hideFooter: true,
maxWidth: "lg",
});
}
diff --git a/libs/auth/src/angular/change-password/change-password.service.abstraction.ts b/libs/auth/src/angular/change-password/change-password.service.abstraction.ts
index f3a6363edb5..c6150968887 100644
--- a/libs/auth/src/angular/change-password/change-password.service.abstraction.ts
+++ b/libs/auth/src/angular/change-password/change-password.service.abstraction.ts
@@ -34,6 +34,16 @@ export abstract class ChangePasswordService {
*/
abstract changePassword(passwordInputResult: PasswordInputResult, userId: UserId): Promise;
+ /**
+ * Changes the user's password and re-encrypts the user key with the `newMasterKey`.
+ * - Specifically, this method uses credentials from the `passwordInputResult` to:
+ * 1. Decrypt the user key with the `currentMasterKey`
+ * 2. Re-encrypt that user key with the `newMasterKey`, resulting in a `newMasterKeyEncryptedUserKey`
+ * 3. Build a `PasswordRequest` object that gets PUTed to `"/accounts/update-temp-password"` so that the
+ * ForcePasswordReset gets set to false.
+ * @param passwordInputResult
+ * @param userId
+ */
abstract changePasswordForAccountRecovery(
passwordInputResult: PasswordInputResult,
userId: UserId,
diff --git a/libs/auth/src/angular/input-password/input-password.component.ts b/libs/auth/src/angular/input-password/input-password.component.ts
index 7a06a8ea2b8..1b5390e1af7 100644
--- a/libs/auth/src/angular/input-password/input-password.component.ts
+++ b/libs/auth/src/angular/input-password/input-password.component.ts
@@ -10,7 +10,6 @@ import {
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
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 { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -67,8 +66,6 @@ export enum InputPasswordFlow {
ChangePassword,
/**
* All form elements above, plus: [Checkbox] Rotate account encryption key (as the last element in the UI)
- *
- * Consider changing this to an input.
*/
ChangePasswordWithOptionalUserKeyRotation,
}
@@ -111,8 +108,7 @@ export class InputPasswordComponent implements OnInit {
@Input() userId?: UserId;
@Input() loading = false;
- @Input() masterPasswordPolicyOptions: MasterPasswordPolicyOptions | undefined = undefined;
- @Input() forceSetPasswordReason?: ForceSetPasswordReason;
+ @Input() masterPasswordPolicyOptions?: MasterPasswordPolicyOptions;
@Input() inlineButtons = false;
@Input() primaryButtonText?: Translation;
@@ -548,10 +544,6 @@ export class InputPasswordComponent implements OnInit {
this.passwordStrengthScore = score;
}
- /**
- * Checks if the flow is either ChangePassword or ChangePasswordWithOptionalUserKeyRotation
- * @private
- */
protected isChangePasswordFlow(): boolean {
return (
this.flow === InputPasswordFlow.ChangePassword ||