mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 05:43:41 +00:00
[PM-15940] Add regen to SSO login (#12643)
* Add loginSuccessHandlerService to SSO login component * Update regen service to handle SSO login
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
|||||||
TrustedDeviceUserDecryptionOption,
|
TrustedDeviceUserDecryptionOption,
|
||||||
UserDecryptionOptions,
|
UserDecryptionOptions,
|
||||||
UserDecryptionOptionsServiceAbstraction,
|
UserDecryptionOptionsServiceAbstraction,
|
||||||
|
LoginSuccessHandlerService,
|
||||||
} from "@bitwarden/auth/common";
|
} from "@bitwarden/auth/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
|
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
|
||||||
@@ -35,7 +36,6 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
|||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
|
||||||
import {
|
import {
|
||||||
AsyncActionsModule,
|
AsyncActionsModule,
|
||||||
ButtonModule,
|
ButtonModule,
|
||||||
@@ -117,7 +117,7 @@ export class SsoComponent implements OnInit {
|
|||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private ssoComponentService: SsoComponentService,
|
private ssoComponentService: SsoComponentService,
|
||||||
private syncService: SyncService,
|
private loginSuccessHandlerService: LoginSuccessHandlerService,
|
||||||
) {
|
) {
|
||||||
environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => {
|
environmentService.environment$.pipe(takeUntilDestroyed()).subscribe((env) => {
|
||||||
this.redirectUri = env.getWebVaultUrl() + "/sso-connector.html";
|
this.redirectUri = env.getWebVaultUrl() + "/sso-connector.html";
|
||||||
@@ -378,8 +378,7 @@ export class SsoComponent implements OnInit {
|
|||||||
|
|
||||||
// Everything after the 2FA check is considered a successful login
|
// Everything after the 2FA check is considered a successful login
|
||||||
// Just have to figure out where to send the user
|
// Just have to figure out where to send the user
|
||||||
|
await this.loginSuccessHandlerService.run(authResult.userId);
|
||||||
await this.syncService.fullSync(true);
|
|
||||||
|
|
||||||
// Save off the OrgSsoIdentifier for use in the TDE flows (or elsewhere)
|
// Save off the OrgSsoIdentifier for use in the TDE flows (or elsewhere)
|
||||||
// - TDE login decryption options component
|
// - TDE login decryption options component
|
||||||
|
|||||||
@@ -153,6 +153,56 @@ describe("regenerateIfNeeded", () => {
|
|||||||
expect(keyService.setPrivateKey).not.toHaveBeenCalled();
|
expect(keyService.setPrivateKey).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not regenerate when user symmetric key is unavailable", async () => {
|
||||||
|
const mockVerificationResponse: VerifyAsymmetricKeysResponse = {
|
||||||
|
privateKeyDecryptable: true,
|
||||||
|
validPrivateKey: false,
|
||||||
|
};
|
||||||
|
setupVerificationResponse(mockVerificationResponse, sdkService);
|
||||||
|
keyService.userKey$.mockReturnValue(of(undefined as unknown as UserKey));
|
||||||
|
|
||||||
|
await sut.regenerateIfNeeded(userId);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys,
|
||||||
|
).not.toHaveBeenCalled();
|
||||||
|
expect(keyService.setPrivateKey).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not regenerate when user's encrypted private key is unavailable", async () => {
|
||||||
|
const mockVerificationResponse: VerifyAsymmetricKeysResponse = {
|
||||||
|
privateKeyDecryptable: true,
|
||||||
|
validPrivateKey: false,
|
||||||
|
};
|
||||||
|
setupVerificationResponse(mockVerificationResponse, sdkService);
|
||||||
|
keyService.userEncryptedPrivateKey$.mockReturnValue(
|
||||||
|
of(undefined as unknown as EncryptedString),
|
||||||
|
);
|
||||||
|
|
||||||
|
await sut.regenerateIfNeeded(userId);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys,
|
||||||
|
).not.toHaveBeenCalled();
|
||||||
|
expect(keyService.setPrivateKey).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not regenerate when user's public key is unavailable", async () => {
|
||||||
|
const mockVerificationResponse: VerifyAsymmetricKeysResponse = {
|
||||||
|
privateKeyDecryptable: true,
|
||||||
|
validPrivateKey: false,
|
||||||
|
};
|
||||||
|
setupVerificationResponse(mockVerificationResponse, sdkService);
|
||||||
|
apiService.getUserPublicKey.mockResolvedValue(undefined as any);
|
||||||
|
|
||||||
|
await sut.regenerateIfNeeded(userId);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys,
|
||||||
|
).not.toHaveBeenCalled();
|
||||||
|
expect(keyService.setPrivateKey).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("should regenerate when private key is decryptable and invalid", async () => {
|
it("should regenerate when private key is decryptable and invalid", async () => {
|
||||||
const mockVerificationResponse: VerifyAsymmetricKeysResponse = {
|
const mockVerificationResponse: VerifyAsymmetricKeysResponse = {
|
||||||
privateKeyDecryptable: true,
|
privateKeyDecryptable: true,
|
||||||
|
|||||||
@@ -49,14 +49,31 @@ export class DefaultUserAsymmetricKeysRegenerationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async shouldRegenerate(userId: UserId): Promise<boolean> {
|
private async shouldRegenerate(userId: UserId): Promise<boolean> {
|
||||||
const [userKey, userKeyEncryptedPrivateKey, publicKeyResponse] = await firstValueFrom(
|
const userKey = await firstValueFrom(this.keyService.userKey$(userId));
|
||||||
|
|
||||||
|
// For SSO logins from untrusted devices, the userKey will not be available, and the private key regeneration process should be skipped.
|
||||||
|
// In such cases, regeneration will occur on the following device login flow.
|
||||||
|
if (!userKey) {
|
||||||
|
this.logService.info(
|
||||||
|
"[UserAsymmetricKeyRegeneration] User symmetric key unavailable, skipping regeneration for the user.",
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [userKeyEncryptedPrivateKey, publicKeyResponse] = await firstValueFrom(
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.keyService.userKey$(userId),
|
|
||||||
this.keyService.userEncryptedPrivateKey$(userId),
|
this.keyService.userEncryptedPrivateKey$(userId),
|
||||||
this.apiService.getUserPublicKey(userId),
|
this.apiService.getUserPublicKey(userId),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!userKeyEncryptedPrivateKey || !publicKeyResponse) {
|
||||||
|
this.logService.warning(
|
||||||
|
"[UserAsymmetricKeyRegeneration] User's asymmetric key initialization data is unavailable, skipping regeneration.",
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const verificationResponse = await firstValueFrom(
|
const verificationResponse = await firstValueFrom(
|
||||||
this.sdkService.client$.pipe(
|
this.sdkService.client$.pipe(
|
||||||
map((sdk) => {
|
map((sdk) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user