mirror of
https://github.com/bitwarden/browser
synced 2025-12-22 19:23:52 +00:00
[PM-23243] In sync response and identity success response add MasterPasswordUnlockDataResponse in decryption options response model. (#15916)
* added master password unlock and decryption option fields into identity token connect response * incorrect master password unlock response parsing * use sdk * use sdk * better type checking on response parsing * not using sdk * revert of bad merge conflicts * revert of bad merge conflicts * master password unlock setter in state * unit test coverage for responses processing * master password unlock in identity user decryption options * unit test coverage * unit test coverage * unit test coverage * unit test coverage * lint error * set master password unlock data in state on identity response and sync response * revert change in auth's user decryption options * remove unnecessary cast * better docs * change to relative imports * MasterPasswordUnlockData serialization issue * explicit undefined type for `syncUserDecryption` * incorrect identity token response tests
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Argon2KdfConfig, KdfType, PBKDF2KdfConfig } from "@bitwarden/key-management";
|
||||
|
||||
import { KdfConfigResponse } from "./kdf-config.response";
|
||||
|
||||
describe("KdfConfigResponse", () => {
|
||||
it("should throw error when kdf type not provided", () => {
|
||||
expect(() => {
|
||||
new KdfConfigResponse({
|
||||
KdfType: undefined,
|
||||
Iterations: 1,
|
||||
});
|
||||
}).toThrow("KDF config response does not contain a valid KDF type");
|
||||
});
|
||||
|
||||
it("should throw error when kdf type is PBKDF2 and iterations not provided", () => {
|
||||
expect(() => {
|
||||
new KdfConfigResponse({
|
||||
KdfType: KdfType.PBKDF2_SHA256,
|
||||
Iterations: undefined,
|
||||
});
|
||||
}).toThrow("KDF config response does not contain a valid number of iterations");
|
||||
});
|
||||
|
||||
it("should throw error when kdf type is Argon2Id and iterations not provided", () => {
|
||||
expect(() => {
|
||||
new KdfConfigResponse({
|
||||
KdfType: KdfType.Argon2id,
|
||||
Iterations: undefined,
|
||||
});
|
||||
}).toThrow("KDF config response does not contain a valid number of iterations");
|
||||
});
|
||||
|
||||
it("should throw error when kdf type is Argon2Id and memory not provided", () => {
|
||||
expect(() => {
|
||||
new KdfConfigResponse({
|
||||
KdfType: KdfType.Argon2id,
|
||||
Iterations: 3,
|
||||
Memory: undefined,
|
||||
Parallelism: 4,
|
||||
});
|
||||
}).toThrow("KDF config response does not contain a valid memory size for Argon2id");
|
||||
});
|
||||
|
||||
it("should throw error when kdf type is Argon2Id and parallelism not provided", () => {
|
||||
expect(() => {
|
||||
new KdfConfigResponse({
|
||||
KdfType: KdfType.Argon2id,
|
||||
Iterations: 3,
|
||||
Memory: 64,
|
||||
Parallelism: undefined,
|
||||
});
|
||||
}).toThrow("KDF config response does not contain a valid parallelism for Argon2id");
|
||||
});
|
||||
|
||||
it("should create response when kdf type is PBKDF2", () => {
|
||||
const response = new KdfConfigResponse({
|
||||
KdfType: KdfType.PBKDF2_SHA256,
|
||||
Iterations: 600_000,
|
||||
});
|
||||
|
||||
expect(response.kdfType).toBe(KdfType.PBKDF2_SHA256);
|
||||
expect(response.iterations).toBe(600_000);
|
||||
expect(response.memory).toBeUndefined();
|
||||
expect(response.parallelism).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should create response when kdf type is Argon2Id", () => {
|
||||
const response = new KdfConfigResponse({
|
||||
KdfType: KdfType.Argon2id,
|
||||
Iterations: 3,
|
||||
Memory: 64,
|
||||
Parallelism: 4,
|
||||
});
|
||||
|
||||
expect(response.kdfType).toBe(KdfType.Argon2id);
|
||||
expect(response.iterations).toBe(3);
|
||||
expect(response.memory).toBe(64);
|
||||
expect(response.parallelism).toBe(4);
|
||||
});
|
||||
|
||||
describe("toKdfConfig", () => {
|
||||
it("should convert to PBKDF2KdfConfig", () => {
|
||||
const response = new KdfConfigResponse({
|
||||
KdfType: KdfType.PBKDF2_SHA256,
|
||||
Iterations: 600_000,
|
||||
});
|
||||
|
||||
const kdfConfig = response.toKdfConfig();
|
||||
expect(kdfConfig).toBeInstanceOf(PBKDF2KdfConfig);
|
||||
const pbkdf2Config = kdfConfig as PBKDF2KdfConfig;
|
||||
expect(pbkdf2Config.iterations).toBe(600_000);
|
||||
});
|
||||
|
||||
it("should convert to Argon2KdfConfig", () => {
|
||||
const response = new KdfConfigResponse({
|
||||
KdfType: KdfType.Argon2id,
|
||||
Iterations: 3,
|
||||
Memory: 64,
|
||||
Parallelism: 4,
|
||||
});
|
||||
|
||||
const kdfConfig = response.toKdfConfig();
|
||||
expect(kdfConfig).toBeInstanceOf(Argon2KdfConfig);
|
||||
const argon2Config = kdfConfig as Argon2KdfConfig;
|
||||
expect(argon2Config.iterations).toBe(3);
|
||||
expect(argon2Config.memory).toBe(64);
|
||||
expect(argon2Config.parallelism).toBe(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Argon2KdfConfig, KdfConfig, KdfType, PBKDF2KdfConfig } from "@bitwarden/key-management";
|
||||
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
|
||||
export class KdfConfigResponse extends BaseResponse {
|
||||
kdfType: KdfType;
|
||||
iterations: number;
|
||||
memory?: number;
|
||||
parallelism?: number;
|
||||
|
||||
constructor(response: unknown) {
|
||||
super(response);
|
||||
|
||||
const kdfType = this.getResponseProperty("KdfType");
|
||||
if (kdfType == null || typeof kdfType !== "number") {
|
||||
throw new Error("KDF config response does not contain a valid KDF type");
|
||||
}
|
||||
this.kdfType = kdfType as KdfType;
|
||||
|
||||
const iterations = this.getResponseProperty("Iterations");
|
||||
if (iterations == null || typeof iterations !== "number") {
|
||||
throw new Error("KDF config response does not contain a valid number of iterations");
|
||||
}
|
||||
this.iterations = iterations;
|
||||
|
||||
if (this.kdfType === KdfType.Argon2id) {
|
||||
const memory = this.getResponseProperty("Memory");
|
||||
if (memory == null || typeof memory !== "number") {
|
||||
throw new Error("KDF config response does not contain a valid memory size for Argon2id");
|
||||
}
|
||||
const parallelism = this.getResponseProperty("Parallelism");
|
||||
if (parallelism == null || typeof parallelism !== "number") {
|
||||
throw new Error("KDF config response does not contain a valid parallelism for Argon2id");
|
||||
}
|
||||
this.memory = memory;
|
||||
this.parallelism = parallelism;
|
||||
}
|
||||
}
|
||||
|
||||
toKdfConfig(): KdfConfig {
|
||||
switch (this.kdfType) {
|
||||
case KdfType.Argon2id:
|
||||
return new Argon2KdfConfig(this.iterations, this.memory!, this.parallelism!);
|
||||
case KdfType.PBKDF2_SHA256:
|
||||
return new PBKDF2KdfConfig(this.iterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KdfType } from "@bitwarden/key-management";
|
||||
|
||||
import { makeEncString } from "../../../../spec";
|
||||
|
||||
import { UserDecryptionResponse } from "./user-decryption.response";
|
||||
|
||||
describe("UserDecryptionResponse", () => {
|
||||
it("should create response when masterPasswordUnlock provided", () => {
|
||||
const salt = "test@example.com";
|
||||
const encryptedUserKey = makeEncString("testUserKey");
|
||||
const kdfIterations = 600_000;
|
||||
|
||||
const response = {
|
||||
MasterPasswordUnlock: {
|
||||
Salt: salt,
|
||||
Kdf: {
|
||||
KdfType: KdfType.PBKDF2_SHA256 as number,
|
||||
Iterations: kdfIterations,
|
||||
},
|
||||
MasterKeyEncryptedUserKey: encryptedUserKey.encryptedString,
|
||||
},
|
||||
};
|
||||
|
||||
const userDecryptionResponse = new UserDecryptionResponse(response);
|
||||
expect(userDecryptionResponse.masterPasswordUnlock).toBeDefined();
|
||||
expect(userDecryptionResponse.masterPasswordUnlock!.salt).toEqual(salt);
|
||||
expect(userDecryptionResponse.masterPasswordUnlock!.kdf).toBeDefined();
|
||||
expect(userDecryptionResponse.masterPasswordUnlock!.kdf!.kdfType).toEqual(
|
||||
KdfType.PBKDF2_SHA256,
|
||||
);
|
||||
expect(userDecryptionResponse.masterPasswordUnlock!.kdf!.iterations).toEqual(kdfIterations);
|
||||
expect(userDecryptionResponse.masterPasswordUnlock!.masterKeyWrappedUserKey).toEqual(
|
||||
encryptedUserKey,
|
||||
);
|
||||
});
|
||||
|
||||
it("should create response when masterPasswordUnlock is not provided", () => {
|
||||
const userDecryptionResponse = new UserDecryptionResponse({});
|
||||
|
||||
expect(userDecryptionResponse.masterPasswordUnlock).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { MasterPasswordUnlockResponse } from "../../master-password/models/response/master-password-unlock.response";
|
||||
|
||||
export class UserDecryptionResponse extends BaseResponse {
|
||||
masterPasswordUnlock?: MasterPasswordUnlockResponse;
|
||||
|
||||
constructor(response: unknown) {
|
||||
super(response);
|
||||
|
||||
const masterPasswordUnlock = this.getResponseProperty("MasterPasswordUnlock");
|
||||
if (masterPasswordUnlock != null || typeof masterPasswordUnlock === "object") {
|
||||
this.masterPasswordUnlock = new MasterPasswordUnlockResponse(masterPasswordUnlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user