1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 17:23:37 +00:00

[PM-21797] Require userID for keyService's getUserKeyFromStorage (#14855)

* require userID for keyService's getUserKeyFromStorage
This commit is contained in:
Thomas Avery
2025-05-30 13:45:31 -05:00
committed by GitHub
parent 9f9cb0d13d
commit eba22cf5f8
3 changed files with 85 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/ke
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { KeySuffixOptions } from "@bitwarden/common/platform/enums";
import { Encrypted } from "@bitwarden/common/platform/interfaces/encrypted";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString, EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string";
@@ -896,4 +897,84 @@ describe("keyService", () => {
});
});
});
describe("getUserKeyFromStorage", () => {
let mockUserKey: UserKey;
let validateUserKeySpy: jest.SpyInstance;
beforeEach(() => {
mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey;
validateUserKeySpy = jest.spyOn(keyService, "validateUserKey");
});
afterEach(() => {
validateUserKeySpy.mockRestore();
});
describe("input validation", () => {
const invalidUserIdTestCases = [
{ keySuffix: KeySuffixOptions.Auto, userId: null as unknown as UserId },
{ keySuffix: KeySuffixOptions.Auto, userId: undefined as unknown as UserId },
{ keySuffix: KeySuffixOptions.Pin, userId: null as unknown as UserId },
{ keySuffix: KeySuffixOptions.Pin, userId: undefined as unknown as UserId },
];
test.each(invalidUserIdTestCases)(
"throws when keySuffix is $keySuffix and userId is $userId",
async ({ keySuffix, userId }) => {
await expect(keyService.getUserKeyFromStorage(keySuffix, userId)).rejects.toThrow(
"UserId is required",
);
},
);
});
describe("with Pin keySuffix", () => {
it("returns null and doesn't validate the key", async () => {
const result = await keyService.getUserKeyFromStorage(KeySuffixOptions.Pin, mockUserId);
expect(result).toBeNull();
expect(validateUserKeySpy).not.toHaveBeenCalled();
});
});
describe("with Auto keySuffix", () => {
it("returns validated key from storage when key exists and is valid", async () => {
stateService.getUserKeyAutoUnlock.mockResolvedValue(mockUserKey.keyB64);
validateUserKeySpy.mockResolvedValue(true);
const result = await keyService.getUserKeyFromStorage(KeySuffixOptions.Auto, mockUserId);
expect(result).toEqual(mockUserKey);
expect(validateUserKeySpy).toHaveBeenCalledWith(mockUserKey, mockUserId);
expect(stateService.getUserKeyAutoUnlock).toHaveBeenCalledWith({
userId: mockUserId,
});
});
it("returns null when no key is found in storage", async () => {
stateService.getUserKeyAutoUnlock.mockResolvedValue(null as unknown as string);
const result = await keyService.getUserKeyFromStorage(KeySuffixOptions.Auto, mockUserId);
expect(result).toBeNull();
expect(validateUserKeySpy).not.toHaveBeenCalled();
});
it("clears stored keys when userKey validation fails", async () => {
stateService.getUserKeyAutoUnlock.mockResolvedValue(mockUserKey.keyB64);
validateUserKeySpy.mockResolvedValue(false);
const result = await keyService.getUserKeyFromStorage(KeySuffixOptions.Auto, mockUserId);
expect(result).toEqual(mockUserKey);
expect(validateUserKeySpy).toHaveBeenCalledWith(mockUserKey, mockUserId);
expect(logService.warning).toHaveBeenCalledWith("Invalid key, throwing away stored keys");
expect(pinService.clearPinKeyEncryptedUserKeyEphemeral).toHaveBeenCalledWith(mockUserId);
expect(stateService.setUserKeyAutoUnlock).toHaveBeenCalledWith(null, {
userId: mockUserId,
});
});
});
});
});