diff --git a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts index 5c9cf57f475..2dad1e3034c 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts @@ -280,10 +280,10 @@ describe("VaultPopupAutofillService", () => { it("should close popup after a timeout for chromium browsers", async () => { mockPlatformUtilsService.isFirefox.mockReturnValue(false); - jest.spyOn(global, "requestAnimationFrame"); + jest.spyOn(global, "setTimeout"); await service.doAutofill(mockCipher); jest.advanceTimersByTime(50); - expect(requestAnimationFrame).toHaveBeenCalled(); + expect(setTimeout).toHaveBeenCalledTimes(1); expect(BrowserApi.closePopup).toHaveBeenCalled(); }); diff --git a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts index c0221af75b6..ff282d7a6d0 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts @@ -280,7 +280,7 @@ export class VaultPopupAutofillService { } // Slight delay to fix bug in Chromium browsers where popup closes without copying totp to clipboard - requestAnimationFrame(() => BrowserApi.closePopup(window)); + setTimeout(() => BrowserApi.closePopup(window), 50); } /** diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index a7219a0ebea..1959d64e334 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.1.6", + "version": "2025.1.3", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index ea2450ed5bd..6feed970798 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.1.6", + "version": "2025.1.3", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 0f8cdf81cf2..03ea969c7bc 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -158,6 +158,7 @@ export class ApiService implements ApiServiceAbstraction { private deviceType: string; private isWebClient = false; private isDesktopClient = false; + private refreshTokenPromise: Promise | undefined; /** * The message (responseJson.ErrorModel.Message) that comes back from the server when a new device verification is required. @@ -1733,7 +1734,18 @@ export class ApiService implements ApiServiceAbstraction { ); } - protected async refreshToken(): Promise { + // Keep the running refreshTokenPromise to prevent parallel calls. + protected refreshToken(): Promise { + if (this.refreshTokenPromise === undefined) { + this.refreshTokenPromise = this.internalRefreshToken(); + void this.refreshTokenPromise.finally(() => { + this.refreshTokenPromise = undefined; + }); + } + return this.refreshTokenPromise; + } + + private async internalRefreshToken(): Promise { const refreshToken = await this.tokenService.getRefreshToken(); if (refreshToken != null && refreshToken !== "") { return this.refreshAccessToken();