diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts index 619652f16a..41215d012a 100644 --- a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts +++ b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts @@ -7,6 +7,7 @@ import { UserVerificationService } from "@bitwarden/common/auth/abstractions/use import { WebauthnRotateCredentialRequest } from "@bitwarden/common/auth/models/request/webauthn-rotate-credential.request"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { SendWithIdRequest } from "@bitwarden/common/tools/send/models/request/send-with-id.request"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; @@ -44,6 +45,7 @@ describe("KeyRotationService", () => { let mockConfigService: MockProxy; let mockSyncService: MockProxy; let mockWebauthnLoginAdminService: MockProxy; + let mockLogService: MockProxy; const mockUser = { id: "mockUserId" as UserId, @@ -66,6 +68,7 @@ describe("KeyRotationService", () => { mockConfigService = mock(); mockSyncService = mock(); mockWebauthnLoginAdminService = mock(); + mockLogService = mock(); keyRotationService = new UserKeyRotationService( mockUserVerificationService, @@ -80,6 +83,7 @@ describe("KeyRotationService", () => { mockEncryptService, mockSyncService, mockWebauthnLoginAdminService, + mockLogService, ); }); diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts index 5c6132c266..8116bcd047 100644 --- a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts @@ -7,6 +7,7 @@ import { UserVerificationService } from "@bitwarden/common/auth/abstractions/use import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { UserId } from "@bitwarden/common/types/guid"; @@ -38,6 +39,7 @@ export class UserKeyRotationService { private encryptService: EncryptService, private syncService: SyncService, private webauthnLoginAdminService: WebauthnLoginAdminService, + private logService: LogService, ) {} /** @@ -48,11 +50,14 @@ export class UserKeyRotationService { masterPassword: string, user: { id: UserId } & AccountInfo, ): Promise { + this.logService.info("[Userkey rotation] Starting user key rotation..."); if (!masterPassword) { + this.logService.info("[Userkey rotation] Invalid master password provided. Aborting!"); throw new Error("Invalid master password"); } if ((await this.syncService.getLastSync()) === null) { + this.logService.info("[Userkey rotation] Client was never synced. Aborting!"); throw new Error( "The local vault is de-synced and the keys cannot be rotated. Please log out and log back in to resolve this issue.", ); @@ -74,6 +79,7 @@ export class UserKeyRotationService { const [newUserKey, newEncUserKey] = await this.keyService.makeUserKey(masterKey); if (!newUserKey || !newEncUserKey) { + this.logService.info("[Userkey rotation] User key could not be created. Aborting!"); throw new Error("User key could not be created"); } @@ -91,6 +97,9 @@ export class UserKeyRotationService { // Note: We distribute the legacy key, but not all domains actually use it. If any of those // domains break their legacy support it will break the migration process for legacy users. const originalUserKey = await this.keyService.getUserKeyWithLegacySupport(user.id); + const isMasterKey = + (await firstValueFrom(this.keyService.userKey$(user.id))) != originalUserKey; + this.logService.info("[Userkey rotation] Is legacy user: " + isMasterKey); // Add re-encrypted data request.privateKey = await this.encryptPrivateKey(newUserKey, user.id); @@ -151,10 +160,14 @@ export class UserKeyRotationService { request.webauthnKeys = rotatedWebauthnKeys; } + this.logService.info("[Userkey rotation] Posting user key rotation request to server"); await this.apiService.postUserKeyUpdate(request); + this.logService.info("[Userkey rotation] Userkey rotation request posted to server"); // TODO PM-2199: Add device trust rotation support to the user key rotation endpoint + this.logService.info("[Userkey rotation] Rotating device trust..."); await this.deviceTrustService.rotateDevicesTrust(user.id, newUserKey, masterPasswordHash); + this.logService.info("[Userkey rotation] Device trust rotation completed"); } private async encryptPrivateKey( diff --git a/libs/common/src/auth/services/device-trust.service.implementation.ts b/libs/common/src/auth/services/device-trust.service.implementation.ts index 88963c524c..1738ab10bb 100644 --- a/libs/common/src/auth/services/device-trust.service.implementation.ts +++ b/libs/common/src/auth/services/device-trust.service.implementation.ts @@ -175,6 +175,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { newUserKey: UserKey, masterPasswordHash: string, ): Promise { + this.logService.info("[Device trust rotation] Rotating device trust..."); if (!userId) { throw new Error("UserId is required. Cannot rotate device's trust."); } @@ -183,11 +184,15 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { if (currentDeviceKey == null) { // If the current device doesn't have a device key available to it, then we can't // rotate any trust at all, so early return. + this.logService.info("[Device trust rotation] No device key available to rotate trust!"); return; } // At this point of rotating their keys, they should still have their old user key in state const oldUserKey = await firstValueFrom(this.keyService.userKey$(userId)); + if (oldUserKey == newUserKey) { + this.logService.info("[Device trust rotation] Old user key is the same as the new user key."); + } const deviceIdentifier = await this.appIdService.getAppId(); const secretVerificationRequest = new SecretVerificationRequest(); @@ -229,7 +234,12 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { trustRequest.currentDevice = currentDeviceUpdateRequest; trustRequest.otherDevices = []; + this.logService.info( + "[Device trust rotation] Posting device trust update with current device:", + deviceIdentifier, + ); await this.devicesApiService.updateTrust(trustRequest, deviceIdentifier); + this.logService.info("[Device trust rotation] Device trust update posted successfully."); } async getDeviceKey(userId: UserId): Promise { diff --git a/libs/common/src/services/notifications.service.ts b/libs/common/src/services/notifications.service.ts index 8e6a664a0a..d443193c9b 100644 --- a/libs/common/src/services/notifications.service.ts +++ b/libs/common/src/services/notifications.service.ts @@ -193,6 +193,7 @@ export class NotificationsService implements NotificationsServiceAbstraction { break; case NotificationType.LogOut: if (isAuthenticated) { + this.logService.info("[Notifications Service] Received logout notification"); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.logoutCallback("logoutNotification");