1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 18:53:29 +00:00

feat(device-approval-persistence): [PM-19380] Device Approval Persistence (#13958)

* feat(device-approval-persistence): [PM-19380] Device Approval Persistence - Added lookup on standard auth requests.

* fix(device-approval-persistence): [PM-19380] Device Approval Persistence - Fixed issue with null value trying to be parsed from the fromJSON function.




---------

Co-authored-by: Todd Martin <tmartin@bitwarden.com>
This commit is contained in:
Patrick-Pimentel-Bitwarden
2025-04-04 15:44:48 -04:00
committed by GitHub
parent b385dd430e
commit 1af8fe2012
8 changed files with 605 additions and 357 deletions

View File

@@ -2,11 +2,9 @@ import { signal } from "@angular/core";
import { TestBed } from "@angular/core/testing";
import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service";
import { AuthRequestType } from "@bitwarden/common/auth/enums/auth-request-type";
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { LoginViaAuthRequestCacheService } from "./default-login-via-auth-request-cache.service";
@@ -39,12 +37,12 @@ describe("LoginViaAuthRequestCache", () => {
});
it("`getCachedLoginViaAuthRequestView` returns the cached data", async () => {
cacheSignal.set({ ...buildAuthenticMockAuthView() });
cacheSignal.set({ ...buildMockState() });
service = testBed.inject(LoginViaAuthRequestCacheService);
await service.init();
expect(service.getCachedLoginViaAuthRequestView()).toEqual({
...buildAuthenticMockAuthView(),
...buildMockState(),
});
});
@@ -54,20 +52,19 @@ describe("LoginViaAuthRequestCache", () => {
const parameters = buildAuthenticMockAuthView();
service.cacheLoginView(
parameters.authRequest,
parameters.authRequestResponse,
parameters.fingerprintPhrase,
{ publicKey: new Uint8Array(), privateKey: new Uint8Array() },
);
service.cacheLoginView(parameters.id, parameters.privateKey, parameters.accessCode);
expect(cacheSignal.set).toHaveBeenCalledWith(parameters);
expect(cacheSignal.set).toHaveBeenCalledWith({
id: parameters.id,
privateKey: Utils.fromBufferToB64(parameters.privateKey),
accessCode: parameters.accessCode,
});
});
});
describe("feature disabled", () => {
beforeEach(async () => {
cacheSignal.set({ ...buildAuthenticMockAuthView() } as LoginViaAuthRequestView);
cacheSignal.set({ ...buildMockState() } as LoginViaAuthRequestView);
getFeatureFlag.mockResolvedValue(false);
cacheSetMock.mockClear();
@@ -82,12 +79,7 @@ describe("LoginViaAuthRequestCache", () => {
it("does not update the signal value", () => {
const params = buildAuthenticMockAuthView();
service.cacheLoginView(
params.authRequest,
params.authRequestResponse,
params.fingerprintPhrase,
{ publicKey: new Uint8Array(), privateKey: new Uint8Array() },
);
service.cacheLoginView(params.id, params.privateKey, params.accessCode);
expect(cacheSignal.set).not.toHaveBeenCalled();
});
@@ -95,17 +87,17 @@ describe("LoginViaAuthRequestCache", () => {
const buildAuthenticMockAuthView = () => {
return {
fingerprintPhrase: "",
privateKey: "",
publicKey: "",
authRequest: new AuthRequest(
"test@gmail.com",
"deviceIdentifier",
"publicKey",
AuthRequestType.Unlock,
"accessCode",
),
authRequestResponse: new AuthRequestResponse({}),
id: "testId",
privateKey: new Uint8Array(),
accessCode: "testAccessCode",
};
};
const buildMockState = () => {
return {
id: "testId",
privateKey: Utils.fromBufferToB64(new Uint8Array()),
accessCode: "testAccessCode",
};
};
});

View File

@@ -1,8 +1,6 @@
import { inject, Injectable, WritableSignal } from "@angular/core";
import { ViewCacheService } from "@bitwarden/angular/platform/abstractions/view-cache.service";
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -45,12 +43,7 @@ export class LoginViaAuthRequestCacheService {
/**
* Update the cache with the new LoginView.
*/
cacheLoginView(
authRequest: AuthRequest,
authRequestResponse: AuthRequestResponse,
fingerprintPhrase: string,
keys: { privateKey: Uint8Array | undefined; publicKey: Uint8Array | undefined },
): void {
cacheLoginView(id: string, privateKey: Uint8Array, accessCode: string): void {
if (!this.featureEnabled) {
return;
}
@@ -59,11 +52,9 @@ export class LoginViaAuthRequestCacheService {
// data can be properly formed when json-ified. If not done, they are not stored properly and
// will not be parsable by the cryptography library after coming out of storage.
this.defaultLoginViaAuthRequestCache.set({
authRequest,
authRequestResponse,
fingerprintPhrase,
privateKey: keys.privateKey ? Utils.fromBufferToB64(keys.privateKey.buffer) : undefined,
publicKey: keys.publicKey ? Utils.fromBufferToB64(keys.publicKey.buffer) : undefined,
id: id,
privateKey: Utils.fromBufferToB64(privateKey.buffer),
accessCode: accessCode,
} as LoginViaAuthRequestView);
}