1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-27 18:13:29 +00:00

Merge branch 'main' into auth/pm-9115/implement-view-data-persistence-in-2FA-flows

This commit is contained in:
Alec Rippberger
2025-03-12 17:19:23 -05:00
committed by GitHub
84 changed files with 1025 additions and 3027 deletions

View File

@@ -38,7 +38,6 @@ import {
ProviderUserUserDetailsResponse,
} from "../admin-console/models/response/provider/provider-user.response";
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
import { AuthRequest } from "../auth/models/request/auth.request";
import { DeviceVerificationRequest } from "../auth/models/request/device-verification.request";
import { DisableTwoFactorAuthenticatorRequest } from "../auth/models/request/disable-two-factor-authenticator.request";
import { EmailTokenRequest } from "../auth/models/request/email-token.request";
@@ -49,17 +48,13 @@ import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-
import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request";
import { KeyConnectorUserKeyRequest } from "../auth/models/request/key-connector-user-key.request";
import { PasswordHintRequest } from "../auth/models/request/password-hint.request";
import { PasswordRequest } from "../auth/models/request/password.request";
import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request";
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
import { SetKeyConnectorKeyRequest } from "../auth/models/request/set-key-connector-key.request";
import { SetPasswordRequest } from "../auth/models/request/set-password.request";
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request";
import { UpdateProfileRequest } from "../auth/models/request/update-profile.request";
import { UpdateTdeOffboardingPasswordRequest } from "../auth/models/request/update-tde-offboarding-password.request";
import { UpdateTempPasswordRequest } from "../auth/models/request/update-temp-password.request";
import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request";
import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request";
import { UpdateTwoFactorEmailRequest } from "../auth/models/request/update-two-factor-email.request";
@@ -169,8 +164,6 @@ export abstract class ApiService {
postPrelogin: (request: PreloginRequest) => Promise<PreloginResponse>;
postEmailToken: (request: EmailTokenRequest) => Promise<any>;
postEmail: (request: EmailRequest) => Promise<any>;
postPassword: (request: PasswordRequest) => Promise<any>;
setPassword: (request: SetPasswordRequest) => Promise<any>;
postSetKeyConnectorKey: (request: SetKeyConnectorKeyRequest) => Promise<any>;
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
getAccountRevisionDate: () => Promise<number>;
@@ -189,13 +182,8 @@ export abstract class ApiService {
postAccountKdf: (request: KdfRequest) => Promise<any>;
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
putUpdateTdeOffboardingPassword: (request: UpdateTdeOffboardingPasswordRequest) => Promise<any>;
postConvertToKeyConnector: () => Promise<void>;
//passwordless
postAuthRequest: (request: AuthRequest) => Promise<AuthRequestResponse>;
postAdminAuthRequest: (request: AuthRequest) => Promise<AuthRequestResponse>;
getAuthResponse: (id: string, accessCode: string) => Promise<AuthRequestResponse>;
getAuthRequest: (id: string) => Promise<AuthRequestResponse>;
putAuthRequest: (id: string, request: PasswordlessAuthRequest) => Promise<AuthRequestResponse>;
getAuthRequests: () => Promise<ListResponse<AuthRequestResponse>>;

View File

@@ -0,0 +1,28 @@
import { PasswordRequest } from "../models/request/password.request";
import { SetPasswordRequest } from "../models/request/set-password.request";
import { UpdateTdeOffboardingPasswordRequest } from "../models/request/update-tde-offboarding-password.request";
import { UpdateTempPasswordRequest } from "../models/request/update-temp-password.request";
export abstract class MasterPasswordApiService {
/**
* POSTs a SetPasswordRequest to "/accounts/set-password"
*/
abstract setPassword: (request: SetPasswordRequest) => Promise<any>;
/**
* POSTs a PasswordRequest to "/accounts/password"
*/
abstract postPassword: (request: PasswordRequest) => Promise<any>;
/**
* PUTs an UpdateTempPasswordRequest to "/accounts/update-temp-password"
*/
abstract putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
/**
* PUTs an UpdateTdeOffboardingPasswordRequest to "/accounts/update-tde-offboarding-password"
*/
abstract putUpdateTdeOffboardingPassword: (
request: UpdateTdeOffboardingPasswordRequest,
) => Promise<any>;
}

View File

@@ -0,0 +1,85 @@
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MasterPasswordApiService as MasterPasswordApiServiceAbstraction } from "../../abstractions/master-password-api.service.abstraction";
import { PasswordRequest } from "../../models/request/password.request";
import { SetPasswordRequest } from "../../models/request/set-password.request";
import { UpdateTdeOffboardingPasswordRequest } from "../../models/request/update-tde-offboarding-password.request";
import { UpdateTempPasswordRequest } from "../../models/request/update-temp-password.request";
export class MasterPasswordApiService implements MasterPasswordApiServiceAbstraction {
constructor(
private apiService: ApiService,
private logService: LogService,
) {}
async setPassword(request: SetPasswordRequest): Promise<any> {
try {
const response = await this.apiService.send(
"POST",
"/accounts/set-password",
request,
true,
false,
);
return response;
} catch (e: unknown) {
this.logService.error(e);
throw e;
}
}
async postPassword(request: PasswordRequest): Promise<any> {
try {
const response = await this.apiService.send(
"POST",
"/accounts/password",
request,
true,
false,
);
return response;
} catch (e: unknown) {
this.logService.error(e);
throw e;
}
}
async putUpdateTempPassword(request: UpdateTempPasswordRequest): Promise<any> {
try {
const response = await this.apiService.send(
"PUT",
"/accounts/update-temp-password",
request,
true,
false,
);
return response;
} catch (e: unknown) {
this.logService.error(e);
throw e;
}
}
async putUpdateTdeOffboardingPassword(
request: UpdateTdeOffboardingPasswordRequest,
): Promise<any> {
try {
const response = await this.apiService.send(
"PUT",
"/accounts/update-tde-offboarding-password",
request,
true,
false,
);
return response;
} catch (e: unknown) {
this.logService.error(e);
throw e;
}
}
}

View File

@@ -0,0 +1,130 @@
import { mock, MockProxy } from "jest-mock-extended";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { KdfType } from "@bitwarden/key-management";
import { PasswordRequest } from "../../models/request/password.request";
import { SetPasswordRequest } from "../../models/request/set-password.request";
import { UpdateTdeOffboardingPasswordRequest } from "../../models/request/update-tde-offboarding-password.request";
import { UpdateTempPasswordRequest } from "../../models/request/update-temp-password.request";
import { MasterPasswordApiService } from "./master-password-api.service.implementation";
describe("MasterPasswordApiService", () => {
let apiService: MockProxy<ApiService>;
let logService: MockProxy<LogService>;
let sut: MasterPasswordApiService;
beforeEach(() => {
apiService = mock<ApiService>();
logService = mock<LogService>();
sut = new MasterPasswordApiService(apiService, logService);
});
it("should instantiate", () => {
expect(sut).not.toBeFalsy();
});
describe("setPassword", () => {
it("should call apiService.send with the correct parameters", async () => {
// Arrange
const request = new SetPasswordRequest(
"masterPasswordHash",
"key",
"masterPasswordHint",
"orgIdentifier",
{
publicKey: "publicKey",
encryptedPrivateKey: "encryptedPrivateKey",
},
KdfType.PBKDF2_SHA256,
600_000,
);
// Act
await sut.setPassword(request);
// Assert
expect(apiService.send).toHaveBeenCalledWith(
"POST",
"/accounts/set-password",
request,
true,
false,
);
});
});
describe("postPassword", () => {
it("should call apiService.send with the correct parameters", async () => {
// Arrange
const request = {
newMasterPasswordHash: "newMasterPasswordHash",
masterPasswordHint: "masterPasswordHint",
key: "key",
masterPasswordHash: "masterPasswordHash",
} as PasswordRequest;
// Act
await sut.postPassword(request);
// Assert
expect(apiService.send).toHaveBeenCalledWith(
"POST",
"/accounts/password",
request,
true,
false,
);
});
});
describe("putUpdateTempPassword", () => {
it("should call apiService.send with the correct parameters", async () => {
// Arrange
const request = {
masterPasswordHint: "masterPasswordHint",
newMasterPasswordHash: "newMasterPasswordHash",
key: "key",
} as UpdateTempPasswordRequest;
// Act
await sut.putUpdateTempPassword(request);
// Assert
expect(apiService.send).toHaveBeenCalledWith(
"PUT",
"/accounts/update-temp-password",
request,
true,
false,
);
});
});
describe("putUpdateTdeOffboardingPassword", () => {
it("should call apiService.send with the correct parameters", async () => {
// Arrange
const request = {
masterPasswordHint: "masterPasswordHint",
newMasterPasswordHash: "newMasterPasswordHash",
key: "key",
} as UpdateTdeOffboardingPasswordRequest;
// Act
await sut.putUpdateTdeOffboardingPassword(request);
// Assert
expect(apiService.send).toHaveBeenCalledWith(
"PUT",
"/accounts/update-tde-offboarding-password",
request,
true,
false,
);
});
});
});

View File

@@ -27,18 +27,18 @@ export enum FeatureFlag {
EnableRiskInsightsNotifications = "enable-risk-insights-notifications",
DesktopSendUIRefresh = "desktop-send-ui-refresh",
PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service",
VaultBulkManagementAction = "vault-bulk-management-action",
UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh",
SSHKeyVaultItem = "ssh-key-vault-item",
SSHAgent = "ssh-agent",
CipherKeyEncryption = "cipher-key-encryption",
TrialPaymentOptional = "PM-8163-trial-payment",
SecurityTasks = "security-tasks",
/* Vault */
PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form",
NewDeviceVerificationTemporaryDismiss = "new-device-temporary-dismiss",
NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss",
VaultBulkManagementAction = "vault-bulk-management-action",
SecurityTasks = "security-tasks",
PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service",
UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh",
CipherKeyEncryption = "cipher-key-encryption",
TrialPaymentOptional = "PM-8163-trial-payment",
MacOsNativeCredentialSync = "macos-native-credential-sync",
PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form",
PrivateKeyRegeneration = "pm-12241-private-key-regeneration",
ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs",
AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner",
@@ -83,18 +83,18 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.EnableRiskInsightsNotifications]: FALSE,
[FeatureFlag.DesktopSendUIRefresh]: FALSE,
[FeatureFlag.PM4154_BulkEncryptionService]: FALSE,
[FeatureFlag.VaultBulkManagementAction]: FALSE,
[FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE,
[FeatureFlag.SSHKeyVaultItem]: FALSE,
[FeatureFlag.SSHAgent]: FALSE,
[FeatureFlag.CipherKeyEncryption]: FALSE,
[FeatureFlag.TrialPaymentOptional]: FALSE,
[FeatureFlag.SecurityTasks]: FALSE,
/* Vault */
[FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE,
[FeatureFlag.NewDeviceVerificationTemporaryDismiss]: FALSE,
[FeatureFlag.NewDeviceVerificationPermanentDismiss]: FALSE,
[FeatureFlag.VaultBulkManagementAction]: FALSE,
[FeatureFlag.SecurityTasks]: FALSE,
[FeatureFlag.PM4154_BulkEncryptionService]: FALSE,
[FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE,
[FeatureFlag.CipherKeyEncryption]: FALSE,
[FeatureFlag.TrialPaymentOptional]: FALSE,
[FeatureFlag.MacOsNativeCredentialSync]: FALSE,
[FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE,
[FeatureFlag.PrivateKeyRegeneration]: FALSE,
[FeatureFlag.ResellerManagedOrgAlert]: FALSE,
[FeatureFlag.AccountDeprovisioningBanner]: FALSE,

View File

@@ -201,3 +201,4 @@ export const NEW_DEVICE_VERIFICATION_NOTICE = new StateDefinition(
export const VAULT_APPEARANCE = new StateDefinition("vaultAppearance", "disk");
export const SECURITY_TASKS_DISK = new StateDefinition("securityTasks", "disk");
export const AT_RISK_PASSWORDS_PAGE_DISK = new StateDefinition("atRiskPasswordsPage", "disk");
export const NOTIFICATION_DISK = new StateDefinition("notifications", "disk");

View File

@@ -43,7 +43,6 @@ import {
} from "../admin-console/models/response/provider/provider-user.response";
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
import { TokenService } from "../auth/abstractions/token.service";
import { AuthRequest } from "../auth/models/request/auth.request";
import { DeviceVerificationRequest } from "../auth/models/request/device-verification.request";
import { DisableTwoFactorAuthenticatorRequest } from "../auth/models/request/disable-two-factor-authenticator.request";
import { EmailTokenRequest } from "../auth/models/request/email-token.request";
@@ -56,17 +55,13 @@ import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-
import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request";
import { KeyConnectorUserKeyRequest } from "../auth/models/request/key-connector-user-key.request";
import { PasswordHintRequest } from "../auth/models/request/password-hint.request";
import { PasswordRequest } from "../auth/models/request/password.request";
import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request";
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
import { SetKeyConnectorKeyRequest } from "../auth/models/request/set-key-connector-key.request";
import { SetPasswordRequest } from "../auth/models/request/set-password.request";
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request";
import { UpdateProfileRequest } from "../auth/models/request/update-profile.request";
import { UpdateTdeOffboardingPasswordRequest } from "../auth/models/request/update-tde-offboarding-password.request";
import { UpdateTempPasswordRequest } from "../auth/models/request/update-temp-password.request";
import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request";
import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request";
import { UpdateTwoFactorEmailRequest } from "../auth/models/request/update-two-factor-email.request";
@@ -279,22 +274,6 @@ export class ApiService implements ApiServiceAbstraction {
}
// TODO: PM-3519: Create and move to AuthRequest Api service
// TODO: PM-9724: Remove legacy auth request methods when we remove legacy LoginViaAuthRequestV1Components
async postAuthRequest(request: AuthRequest): Promise<AuthRequestResponse> {
const r = await this.send("POST", "/auth-requests/", request, false, true);
return new AuthRequestResponse(r);
}
async postAdminAuthRequest(request: AuthRequest): Promise<AuthRequestResponse> {
const r = await this.send("POST", "/auth-requests/admin-request", request, true, true);
return new AuthRequestResponse(r);
}
async getAuthResponse(id: string, accessCode: string): Promise<AuthRequestResponse> {
const path = `/auth-requests/${id}/response?code=${accessCode}`;
const r = await this.send("GET", path, null, false, true);
return new AuthRequestResponse(r);
}
async getAuthRequest(id: string): Promise<AuthRequestResponse> {
const path = `/auth-requests/${id}`;
const r = await this.send("GET", path, null, true, true);
@@ -374,14 +353,6 @@ export class ApiService implements ApiServiceAbstraction {
return this.send("POST", "/accounts/email", request, true, false);
}
postPassword(request: PasswordRequest): Promise<any> {
return this.send("POST", "/accounts/password", request, true, false);
}
setPassword(request: SetPasswordRequest): Promise<any> {
return this.send("POST", "/accounts/set-password", request, true, false);
}
postSetKeyConnectorKey(request: SetKeyConnectorKeyRequest): Promise<any> {
return this.send("POST", "/accounts/set-key-connector-key", request, true, false);
}
@@ -479,14 +450,6 @@ export class ApiService implements ApiServiceAbstraction {
return new ApiKeyResponse(r);
}
putUpdateTempPassword(request: UpdateTempPasswordRequest): Promise<any> {
return this.send("PUT", "/accounts/update-temp-password", request, true, false);
}
putUpdateTdeOffboardingPassword(request: UpdateTdeOffboardingPasswordRequest): Promise<void> {
return this.send("PUT", "/accounts/update-tde-offboarding-password", request, true, false);
}
postConvertToKeyConnector(): Promise<void> {
return this.send("POST", "/accounts/convert-to-key-connector", null, true, false);
}

View File

@@ -11,3 +11,4 @@ export type CipherId = Opaque<string, "CipherId">;
export type SendId = Opaque<string, "SendId">;
export type IndexedEntityId = Opaque<string, "IndexedEntityId">;
export type SecurityTaskId = Opaque<string, "SecurityTaskId">;
export type NotificationId = Opaque<string, "NotificationId">;