1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-01 09:13:54 +00:00

Fix tests

This commit is contained in:
Bernd Schoolmann
2025-11-17 15:31:58 +01:00
parent 791397d60d
commit 23fee8a408
2 changed files with 44 additions and 68 deletions

View File

@@ -2,6 +2,7 @@ import { FormBuilder } from "@angular/forms";
import { mock } from "jest-mock-extended";
import { LoginStrategyServiceAbstraction } from "@bitwarden/auth/common";
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
import { ClientType } from "@bitwarden/common/enums";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -45,6 +46,7 @@ describe("LoginComponent continue() integration", () => {
configService.getFeatureFlag.mockResolvedValue(flagEnabled);
const ssoLoginService: any = { ssoRequiredCache$: { pipe: () => ({}) } };
const environmentService: any = { environment$: { pipe: () => ({}) } };
const webauthnLoginService = mock<WebAuthnLoginServiceAbstraction>();
const component = new LoginComponent(
activatedRoute,
@@ -71,6 +73,7 @@ describe("LoginComponent continue() integration", () => {
configService,
ssoLoginService,
environmentService,
webauthnLoginService,
);
jest.spyOn(component as any, "toggleLoginUiState").mockResolvedValue(undefined);

View File

@@ -8,6 +8,11 @@ import { LogService } from "../../../platform/abstractions/log.service";
import { Utils } from "../../../platform/misc/utils";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { PrfKey } from "../../../types/key";
import {
NavigatorCredentialsService,
PublicKeyCredential as CustomPublicKeyCredential,
AuthenticatorAssertionResponse as CustomAuthenticatorAssertionResponse,
} from "../../abstractions/webauthn/navigator-credentials.service";
import { WebAuthnLoginApiServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-api.service.abstraction";
import { WebAuthnLoginPrfKeyServiceAbstraction } from "../../abstractions/webauthn/webauthn-login-prf-key.service.abstraction";
import { AuthResult } from "../../models/domain/auth-result";
@@ -26,6 +31,7 @@ describe("WebAuthnLoginService", () => {
const webAuthnLoginPrfKeyService = mock<WebAuthnLoginPrfKeyServiceAbstraction>();
const navigatorCredentials = mock<CredentialsContainer>();
const logService = mock<LogService>();
const mockNavigatorCredentialsService = mock<NavigatorCredentialsService>();
let originalPublicKeyCredential!: PublicKeyCredential | any;
let originalAuthenticatorAssertionResponse!: AuthenticatorAssertionResponse | any;
@@ -36,11 +42,6 @@ describe("WebAuthnLoginService", () => {
originalPublicKeyCredential = global.PublicKeyCredential;
originalAuthenticatorAssertionResponse = global.AuthenticatorAssertionResponse;
// We must do this to make the mocked classes available for all the
// assertCredential(...) tests.
global.PublicKeyCredential = MockPublicKeyCredential as any;
global.AuthenticatorAssertionResponse = MockAuthenticatorAssertionResponse;
// Save the original navigator
originalNavigator = global.window.navigator;
@@ -75,11 +76,10 @@ describe("WebAuthnLoginService", () => {
webAuthnLoginApiService,
loginStrategyService,
webAuthnLoginPrfKeyService,
window,
mockNavigatorCredentialsService,
logService,
);
}
it("instantiates", () => {
webAuthnLoginService = createWebAuthnLoginService();
expect(webAuthnLoginService).not.toBeFalsy();
@@ -138,16 +138,15 @@ describe("WebAuthnLoginService", () => {
const expectedSaltHex = "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
const saltArrayBuffer = Utils.hexStringToArrayBuffer(expectedSaltHex);
const publicKeyCredential = new MockPublicKeyCredential();
const prfResult: ArrayBuffer =
publicKeyCredential.getClientExtensionResults().prf?.results?.first;
const publicKeyCredential = mockPublicKeyCredential;
const prfResult: ArrayBuffer = publicKeyCredential.prf.buffer as ArrayBuffer;
const prfKey = new SymmetricCryptoKey(new Uint8Array(prfResult)) as PrfKey;
webAuthnLoginPrfKeyService.getLoginWithPrfSalt.mockResolvedValue(saltArrayBuffer);
webAuthnLoginPrfKeyService.createSymmetricKeyFromPrf.mockResolvedValue(prfKey);
// Mock implementations
navigatorCredentials.get.mockResolvedValue(publicKeyCredential);
mockNavigatorCredentialsService.get.mockResolvedValue(publicKeyCredential);
// Act
const result = await webAuthnLoginService.assertCredential(credentialAssertionOptions);
@@ -156,7 +155,7 @@ describe("WebAuthnLoginService", () => {
expect(webAuthnLoginPrfKeyService.getLoginWithPrfSalt).toHaveBeenCalled();
expect(navigatorCredentials.get).toHaveBeenCalledWith(
expect(mockNavigatorCredentialsService.get).toHaveBeenCalledWith(
expect.objectContaining({
publicKey: expect.objectContaining({
...credentialAssertionOptions.options,
@@ -178,7 +177,9 @@ describe("WebAuthnLoginService", () => {
expect(result.deviceResponse).toBeInstanceOf(WebAuthnLoginAssertionResponseRequest);
expect(result.deviceResponse.id).toEqual(publicKeyCredential.id);
expect(result.deviceResponse.rawId).toEqual(publicKeyCredential.rawIdB64Str);
expect(result.deviceResponse.rawId).toEqual(
Utils.fromBufferToUrlB64(publicKeyCredential.rawId.buffer as ArrayBuffer),
);
expect(result.deviceResponse.type).toEqual(publicKeyCredential.type);
// extensions being empty could change in the future but for now it is expected
expect(result.deviceResponse.extensions).toEqual({});
@@ -186,10 +187,18 @@ describe("WebAuthnLoginService", () => {
expect("prf" in result.deviceResponse.extensions).toBe(false);
expect(result.deviceResponse.response).toEqual({
authenticatorData: publicKeyCredential.response.authenticatorDataB64Str,
clientDataJSON: publicKeyCredential.response.clientDataJSONB64Str,
signature: publicKeyCredential.response.signatureB64Str,
userHandle: publicKeyCredential.response.userHandleB64Str,
authenticatorData: Utils.fromBufferToUrlB64(
publicKeyCredential.response.authenticatorData.buffer as ArrayBuffer,
),
clientDataJSON: Utils.fromBufferToUrlB64(
publicKeyCredential.response.clientDataJSON.buffer as ArrayBuffer,
),
signature: Utils.fromBufferToUrlB64(
publicKeyCredential.response.signature.buffer as ArrayBuffer,
),
userHandle: Utils.fromBufferToUrlB64(
publicKeyCredential.response.userHandle.buffer as ArrayBuffer,
),
});
expect(result.prfKey).toEqual(prfKey);
@@ -233,7 +242,7 @@ describe("WebAuthnLoginService", () => {
describe("logIn(...)", () => {
function buildWebAuthnLoginCredentialAssertionView(): WebAuthnLoginCredentialAssertionView {
const publicKeyCredential = new MockPublicKeyCredential();
const publicKeyCredential = mockPublicKeyCredential;
const deviceResponse = new WebAuthnLoginAssertionResponseRequest(publicKeyCredential);
@@ -270,57 +279,21 @@ function randomBytes(length: number): Uint8Array {
// AuthenticatorAssertionResponse && PublicKeyCredential are only available in secure contexts
// so we need to mock them and assign them to the global object to make them available
// for the tests
class MockAuthenticatorAssertionResponse implements AuthenticatorAssertionResponse {
clientDataJSON: ArrayBuffer = randomBytes(32).buffer;
authenticatorData: ArrayBuffer = randomBytes(196).buffer;
signature: ArrayBuffer = randomBytes(72).buffer;
userHandle: ArrayBuffer = randomBytes(16).buffer;
const mockAuthenticatorAssertionResponse: CustomAuthenticatorAssertionResponse = {
clientDataJSON: randomBytes(32),
authenticatorData: randomBytes(196),
signature: randomBytes(72),
userHandle: randomBytes(16),
};
clientDataJSONB64Str = Utils.fromBufferToUrlB64(this.clientDataJSON);
authenticatorDataB64Str = Utils.fromBufferToUrlB64(this.authenticatorData);
signatureB64Str = Utils.fromBufferToUrlB64(this.signature);
userHandleB64Str = Utils.fromBufferToUrlB64(this.userHandle);
}
class MockPublicKeyCredential implements PublicKeyCredential {
authenticatorAttachment = "cross-platform";
id = "mockCredentialId";
type = "public-key";
rawId: ArrayBuffer = randomBytes(32).buffer;
rawIdB64Str = Utils.fromBufferToUrlB64(this.rawId);
response: MockAuthenticatorAssertionResponse = new MockAuthenticatorAssertionResponse();
// Use random 64 character hex string (32 bytes - matters for symmetric key creation)
// to represent the prf key binary data and convert to ArrayBuffer
// Creating the array buffer from a known hex value allows us to
// assert on the value in tests
private prfKeyArrayBuffer: ArrayBuffer = Utils.hexStringToArrayBuffer(
"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
);
getClientExtensionResults(): any {
return {
prf: {
results: {
first: this.prfKeyArrayBuffer,
},
},
};
}
static isConditionalMediationAvailable(): Promise<boolean> {
return Promise.resolve(false);
}
static isUserVerifyingPlatformAuthenticatorAvailable(): Promise<boolean> {
return Promise.resolve(false);
}
toJSON() {
throw new Error("Method not implemented.");
}
}
const mockPublicKeyCredential: CustomPublicKeyCredential = {
authenticatorAttachment: "cross-platform",
id: "mockCredentialId",
type: "public-key",
rawId: randomBytes(32),
response: mockAuthenticatorAssertionResponse,
prf: randomBytes(32),
};
function buildCredentialAssertionOptions(): WebAuthnLoginCredentialAssertionOptionsView {
// Mock credential assertion options