From 9b4af5e8027ebc3fefcdda461bb869ba3dd8054b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 13 Jan 2026 13:47:40 +0100 Subject: [PATCH] Resolve conflicts / cleanup --- .../angular/login-via-auth-request/README.md | 31 ++--------- .../login-via-auth-request.component.ts | 55 +++++-------------- .../auth-request-login.strategy.spec.ts | 40 -------------- .../auth-request-login.strategy.ts | 15 +---- .../common/models/domain/login-credentials.ts | 5 -- .../login-strategy.state.spec.ts | 2 - 6 files changed, 22 insertions(+), 126 deletions(-) diff --git a/libs/auth/src/angular/login-via-auth-request/README.md b/libs/auth/src/angular/login-via-auth-request/README.md index 3396ba8698b..d1c4ddf3f0a 100644 --- a/libs/auth/src/angular/login-via-auth-request/README.md +++ b/libs/auth/src/angular/login-via-auth-request/README.md @@ -13,16 +13,9 @@ ## Standard Auth Request Flows -### Flow 1: Unauthed user requests approval from device; Approving device has a masterKey in memory +### Flow 1: This flow was removed -1. Unauthed user clicks "Login with device" -2. Navigates to `/login-with-device` which creates a `StandardAuthRequest` -3. Receives approval from a device with authRequestPublicKey(masterKey) -4. Decrypts masterKey -5. Decrypts userKey -6. Proceeds to vault - -### Flow 2: Unauthed user requests approval from device; Approving device does NOT have a masterKey in memory +### Flow 2: Unauthed user requests approval from device; Approving device does NOT need to have a masterKey in memory 1. Unauthed user clicks "Login with device" 2. Navigates to `/login-with-device` which creates a `StandardAuthRequest` @@ -33,28 +26,18 @@ **Note:** This flow is an uncommon scenario and relates to TDE off-boarding. The following describes how a user could get into this flow: -1. An SSO TD user logs into a device via an Admin auth request approval, therefore this device does NOT have a masterKey +1. An SSO TD user logs into a device via an Admin auth request approval, therefore this device does NOT need to have a masterKey in memory 2. The org admin: - Changes the member decryption options from "Trusted devices" to "Master password" AND - Turns off the "Require single sign-on authentication" policy 3. On another device, the user clicks "Login with device", which they can do because the org no longer requires SSO -4. The user approves from the device they had previously logged into with SSO TD, which does NOT have a masterKey in +4. The user approves from the device they had previously logged into with SSO TD, which does NOT need to have a masterKey in memory -### Flow 3: Authed SSO TD user requests approval from device; Approving device has a masterKey in memory +### Flow 3: This flow was removed -1. SSO TD user authenticates via SSO -2. Navigates to `/login-initiated` -3. Clicks "Approve from your other device" -4. Navigates to `/login-with-device` which creates a `StandardAuthRequest` -5. Receives approval from device with authRequestPublicKey(masterKey) -6. Decrypts masterKey -7. Decrypts userKey -8. Establishes trust (if required) -9. Proceeds to vault - -### Flow 4: Authed SSO TD user requests approval from device; Approving device does NOT have a masterKey in memory +### Flow 4: Authed SSO TD user requests approval from device; Approving device does NOT need to have a masterKey in memory 1. SSO TD user authenticates via SSO 2. Navigates to `/login-initiated` @@ -89,9 +72,7 @@ userKey. This is how admins are able to send over the authRequestPublicKey(userK | Flow | Auth Status | Clicks Button [active route] | Navigates to | Approving device has masterKey in memory\* | | --------------- | ----------- | ----------------------------------------------------- | --------------------------- | ------------------------------------------------- | -| Standard Flow 1 | unauthed | "Login with device" [`/login`] | `/login-with-device` | yes | | Standard Flow 2 | unauthed | "Login with device" [`/login`] | `/login-with-device` | no | -| Standard Flow 3 | authed | "Approve from your other device" [`/login-initiated`] | `/login-with-device` | yes | | Standard Flow 4 | authed | "Approve from your other device" [`/login-initiated`] | `/login-with-device` | no | | Admin Flow | authed | "Request admin approval"
[`/login-initiated`] | `/admin-approval-requested` | NA - admin requests always send encrypted userKey | diff --git a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts index fee5a5c3a07..c9177fc4f69 100644 --- a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts +++ b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts @@ -605,10 +605,10 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { if (authRequestResponse.requestApproved) { const userHasAuthenticatedViaSSO = this.authStatus === AuthenticationStatus.Locked; if (userHasAuthenticatedViaSSO) { - // [Standard Flow 3-4] Handle authenticated SSO TD user flows + // [Standard Flow 4] Handle authenticated SSO TD user flows return await this.handleAuthenticatedFlows(authRequestResponse); } else { - // [Standard Flow 1-2] Handle unauthenticated user flows + // [Standard Flow 2] Handle unauthenticated user flows return await this.handleUnauthenticatedFlows(authRequestResponse, requestId); } } @@ -629,7 +629,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { } private async handleAuthenticatedFlows(authRequestResponse: AuthRequestResponse) { - // [Standard Flow 3-4] Handle authenticated SSO TD user flows + // [Standard Flow 4] Handle authenticated SSO TD user flows const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; if (!userId) { this.logService.error( @@ -654,7 +654,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { authRequestResponse: AuthRequestResponse, requestId: string, ) { - // [Standard Flow 1-2] Handle unauthenticated user flows + // [Standard Flow 2] Handle unauthenticated user flows const authRequestLoginCredentials = await this.buildAuthRequestLoginCredentials( requestId, authRequestResponse, @@ -743,43 +743,18 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { /** * See verifyAndHandleApprovedAuthReq() for flow details. - * - * We determine the type of `key` based on the presence or absence of `masterPasswordHash`: - * - If `masterPasswordHash` has a value, we receive the `key` as an authRequestPublicKey(masterKey) [plus we have authRequestPublicKey(masterPasswordHash)] - * - If `masterPasswordHash` does not have a value, we receive the `key` as an authRequestPublicKey(userKey) */ - if (authRequestResponse.masterPasswordHash) { - // ...in Standard Auth Request Flow 1 - const { masterKey, masterKeyHash } = - await this.authRequestService.decryptPubKeyEncryptedMasterKeyAndHash( - authRequestResponse.key, - authRequestResponse.masterPasswordHash, - this.authRequestKeyPair.privateKey, - ); - - return new AuthRequestLoginCredentials( - this.email, - this.accessCode, - requestId, - null, // no userKey - masterKey, - masterKeyHash, - ); - } else { - // ...in Standard Auth Request Flow 2 - const userKey = await this.authRequestService.decryptPubKeyEncryptedUserKey( - authRequestResponse.key, - this.authRequestKeyPair.privateKey, - ); - return new AuthRequestLoginCredentials( - this.email, - this.accessCode, - requestId, - userKey, - null, // no masterKey - null, // no masterKeyHash - ); - } + // ...in Standard Auth Request Flow 2 + const userKey = await this.authRequestService.decryptPubKeyEncryptedUserKey( + authRequestResponse.key, + this.authRequestKeyPair.privateKey, + ); + return new AuthRequestLoginCredentials( + this.email, + this.accessCode, + requestId, + userKey, + ); } private async clearExistingAdminAuthRequestAndStartNewRequest(userId: UserId) { diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts index 4703472d480..12f96c336d7 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts @@ -73,11 +73,7 @@ describe("AuthRequestLoginStrategy", () => { const email = "EMAIL"; const accessCode = "ACCESS_CODE"; const authRequestId = "AUTH_REQUEST_ID"; - const decMasterKey = new SymmetricCryptoKey( - new Uint8Array(64).buffer as CsprngArray, - ) as MasterKey; const decUserKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey; - const decMasterKeyHash = "LOCAL_PASSWORD_HASH"; beforeEach(async () => { keyService = mock(); @@ -150,39 +146,6 @@ describe("AuthRequestLoginStrategy", () => { ); }); - it("sets keys after a successful authentication when masterKey and masterKeyHash provided in login credentials", async () => { - credentials = new AuthRequestLoginCredentials( - email, - accessCode, - authRequestId, - null, - decMasterKey, - decMasterKeyHash, - ); - - const masterKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as MasterKey; - const userKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as UserKey; - - masterPasswordService.masterKeySubject.next(masterKey); - masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(userKey); - tokenService.decodeAccessToken.mockResolvedValue({ sub: mockUserId }); - - await authRequestLoginStrategy.logIn(credentials); - - expect(masterPasswordService.mock.setMasterKey).toHaveBeenCalledWith(masterKey, mockUserId); - expect(masterPasswordService.mock.setMasterKeyHash).toHaveBeenCalledWith( - decMasterKeyHash, - mockUserId, - ); - expect(masterPasswordService.mock.setMasterKeyEncryptedUserKey).toHaveBeenCalledWith( - tokenResponse.key, - mockUserId, - ); - expect(keyService.setUserKey).toHaveBeenCalledWith(userKey, mockUserId); - expect(deviceTrustService.trustDeviceIfRequired).toHaveBeenCalled(); - expect(keyService.setPrivateKey).toHaveBeenCalledWith(tokenResponse.privateKey, mockUserId); - }); - it("sets keys after a successful authentication when only userKey provided in login credentials", async () => { // Initialize credentials with only userKey credentials = new AuthRequestLoginCredentials( @@ -190,8 +153,6 @@ describe("AuthRequestLoginStrategy", () => { accessCode, authRequestId, decUserKey, // Pass userKey - null, // No masterKey - null, // No masterKeyHash ); // Call logIn @@ -234,7 +195,6 @@ describe("AuthRequestLoginStrategy", () => { }; apiService.postIdentityToken.mockResolvedValue(tokenResponse); - masterPasswordService.masterKeySubject.next(decMasterKey); masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(decUserKey); await authRequestLoginStrategy.logIn(credentials); diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts index 16af5fa77dc..cbd2bbe0c81 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts @@ -72,20 +72,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy { } protected override async setMasterKey(response: IdentityTokenResponse, userId: UserId) { - const authRequestCredentials = this.cache.value.authRequestCredentials; - if ( - authRequestCredentials.decryptedMasterKey && - authRequestCredentials.decryptedMasterKeyHash - ) { - await this.masterPasswordService.setMasterKey( - authRequestCredentials.decryptedMasterKey, - userId, - ); - await this.masterPasswordService.setMasterKeyHash( - authRequestCredentials.decryptedMasterKeyHash, - userId, - ); - } + // This login strategy does not use a master key } protected override async setUserKey( diff --git a/libs/auth/src/common/models/domain/login-credentials.ts b/libs/auth/src/common/models/domain/login-credentials.ts index 96ee88945eb..ac334d05df6 100644 --- a/libs/auth/src/common/models/domain/login-credentials.ts +++ b/libs/auth/src/common/models/domain/login-credentials.ts @@ -54,8 +54,6 @@ export class AuthRequestLoginCredentials { public accessCode: string, public authRequestId: string, public decryptedUserKey: UserKey | null, - public decryptedMasterKey: MasterKey | null, - public decryptedMasterKeyHash: string | null, public twoFactor?: TokenTwoFactorRequest, ) {} @@ -66,8 +64,6 @@ export class AuthRequestLoginCredentials { json.accessCode, json.authRequestId, null, - null, - json.decryptedMasterKeyHash, json.twoFactor ? new TokenTwoFactorRequest( json.twoFactor.provider, @@ -78,7 +74,6 @@ export class AuthRequestLoginCredentials { ), { decryptedUserKey: SymmetricCryptoKey.fromJSON(json.decryptedUserKey) as UserKey, - decryptedMasterKey: SymmetricCryptoKey.fromJSON(json.decryptedMasterKey) as MasterKey, }, ); } diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts b/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts index 32c5fdcc4d5..195ae0dd721 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.state.spec.ts @@ -93,8 +93,6 @@ describe("LOGIN_STRATEGY_CACHE_KEY", () => { "ACCESS_CODE", "AUTH_REQUEST_ID", new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, - new SymmetricCryptoKey(new Uint8Array(64)) as MasterKey, - "MASTER_KEY_HASH", ); const result = sut.deserializer(JSON.parse(JSON.stringify(actual)));