1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-20 19:34:03 +00:00

feat(change-password-component): Change Password Update [18720] - Org invite is seemingly working, found one bug to iron out.

This commit is contained in:
Patrick Pimentel
2025-05-30 10:40:27 -04:00
parent 06039927bb
commit bb11db344f
10 changed files with 96 additions and 30 deletions

View File

@@ -18,9 +18,11 @@ import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { ClientType, HttpStatusCode } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -123,6 +125,7 @@ export class LoginComponent implements OnInit, OnDestroy {
private logService: LogService,
private validationService: ValidationService,
private loginSuccessHandlerService: LoginSuccessHandlerService,
private configService: ConfigService,
) {
this.clientType = this.platformUtilsService.getClientType();
}
@@ -226,7 +229,20 @@ export class LoginComponent implements OnInit, OnDestroy {
return;
}
const credentials = new PasswordLoginCredentials(email, masterPassword);
let credentials: PasswordLoginCredentials;
if (
(await this.configService.getFeatureFlag(
FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
)) &&
this.loginComponentService.getOrgPoliciesFromOrgInvite
) {
const orgPoliciesFromInvite = await this.loginComponentService.getOrgPoliciesFromOrgInvite();
const orgPolicies = orgPoliciesFromInvite?.enforcedPasswordPolicyOptions ?? undefined;
credentials = new PasswordLoginCredentials(email, masterPassword, undefined, orgPolicies);
} else {
credentials = new PasswordLoginCredentials(email, masterPassword);
}
try {
const authResult = await this.loginStrategyService.logIn(credentials);
@@ -312,26 +328,30 @@ export class LoginComponent implements OnInit, OnDestroy {
// Determine where to send the user next
// The AuthGuard will handle routing to update-temp-password based on state
// TODO: PM-18269 - evaluate if we can combine this with the
// password evaluation done in the password login strategy.
// If there's an existing org invite, use it to get the org's password policies
// so we can evaluate the MP against the org policies
if (this.loginComponentService.getOrgPoliciesFromOrgInvite) {
const orgPolicies: PasswordPolicies | null =
await this.loginComponentService.getOrgPoliciesFromOrgInvite();
if (
!(await this.configService.getFeatureFlag(FeatureFlag.PM16117_ChangeExistingPasswordRefactor))
) {
// TODO: PM-18269 - evaluate if we can combine this with the
// password evaluation done in the password login strategy.
// If there's an existing org invite, use it to get the org's password policies
// so we can evaluate the MP against the org policies
if (this.loginComponentService.getOrgPoliciesFromOrgInvite) {
const orgPolicies: PasswordPolicies | null =
await this.loginComponentService.getOrgPoliciesFromOrgInvite();
if (orgPolicies) {
// Since we have retrieved the policies, we can go ahead and set them into state for future use
// e.g., the update-password page currently only references state for policy data and
// doesn't fallback to pulling them from the server like it should if they are null.
await this.setPoliciesIntoState(authResult.userId, orgPolicies.policies);
if (orgPolicies) {
// Since we have retrieved the policies, we can go ahead and set them into state for future use
// e.g., the update-password page currently only references state for policy data and
// doesn't fallback to pulling them from the server like it should if they are null.
await this.setPoliciesIntoState(authResult.userId, orgPolicies.policies);
const isPasswordChangeRequired = await this.isPasswordChangeRequiredByOrgPolicy(
orgPolicies.enforcedPasswordPolicyOptions,
);
if (isPasswordChangeRequired) {
await this.router.navigate(["update-password"]);
return;
const isPasswordChangeRequired = await this.isPasswordChangeRequiredByOrgPolicy(
orgPolicies.enforcedPasswordPolicyOptions,
);
if (isPasswordChangeRequired) {
await this.router.navigate(["update-password"]);
return;
}
}
}
}

View File

@@ -26,6 +26,7 @@ import {
} from "@bitwarden/common/key-management/vault-timeout";
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -92,6 +93,7 @@ export abstract class LoginStrategy {
protected vaultTimeoutSettingsService: VaultTimeoutSettingsService,
protected KdfConfigService: KdfConfigService,
protected environmentService: EnvironmentService,
protected configService: ConfigService,
) {}
abstract exportCache(): CacheData;

View File

@@ -12,6 +12,7 @@ import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/ide
import { IdentityDeviceVerificationResponse } from "@bitwarden/common/auth/models/response/identity-device-verification.response";
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/response/identity-two-factor.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { HashPurpose } from "@bitwarden/common/platform/enums";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
@@ -75,7 +76,7 @@ export class PasswordLoginStrategy extends LoginStrategy {
this.localMasterKeyHash$ = this.cache.pipe(map((state) => state.localMasterKeyHash));
}
override async logIn(credentials: PasswordLoginCredentials) {
override async logIn(credentials: PasswordLoginCredentials): Promise<AuthResult> {
const { email, masterPassword, twoFactor } = credentials;
const data = new PasswordLoginStrategyData();
@@ -167,11 +168,24 @@ export class PasswordLoginStrategy extends LoginStrategy {
}
// The identity result can contain master password policies for the user's organizations
const masterPasswordPolicyOptions =
this.getMasterPasswordPolicyOptionsFromResponse(identityResponse);
let masterPasswordPolicyOptions;
if (!masterPasswordPolicyOptions?.enforceOnLogin) {
return;
if (
await this.configService.getFeatureFlag(FeatureFlag.PM16117_ChangeExistingPasswordRefactor)
) {
masterPasswordPolicyOptions = credentials.masterPasswordPolicies;
authResult.orgInviteAndWeakPassword = true;
if (!masterPasswordPolicyOptions.enforceOnLogin) {
return;
}
} else {
masterPasswordPolicyOptions =
this.getMasterPasswordPolicyOptionsFromResponse(identityResponse);
if (!masterPasswordPolicyOptions?.enforceOnLogin) {
return;
}
}
// If there is a policy active, evaluate the supplied password before its no longer in memory

View File

@@ -2,6 +2,7 @@
// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type";
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
import { WebAuthnLoginAssertionResponseRequest } from "@bitwarden/common/auth/services/webauthn-login/request/webauthn-login-assertion-response.request";
@@ -15,6 +16,7 @@ export class PasswordLoginCredentials {
public email: string,
public masterPassword: string,
public twoFactor?: TokenTwoFactorRequest,
public masterPasswordPolicies?: MasterPasswordPolicyOptions,
) {}
}

View File

@@ -26,6 +26,7 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/va
import { PreloginRequest } from "@bitwarden/common/models/request/prelogin.request";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -131,6 +132,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
protected vaultTimeoutSettingsService: VaultTimeoutSettingsService,
protected kdfConfigService: KdfConfigService,
protected taskSchedulerService: TaskSchedulerService,
protected configService: ConfigService,
) {
this.currentAuthnTypeState = this.stateProvider.get(CURRENT_LOGIN_STRATEGY_KEY);
this.loginStrategyCacheState = this.stateProvider.get(CACHE_KEY);
@@ -400,6 +402,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
this.vaultTimeoutSettingsService,
this.kdfConfigService,
this.environmentService,
this.configService,
];
return source.pipe(