1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 21:33:27 +00:00

[PM-23628] Require userId for fetching provider keys (#16993)

* remove getProviderKey and expose providerKeys$

* update consumers
This commit is contained in:
Thomas Avery
2025-10-27 11:04:17 -05:00
committed by GitHub
parent b335987213
commit bd89c0ce6d
9 changed files with 223 additions and 64 deletions

View File

@@ -31,6 +31,7 @@ import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-conso
import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { assertNonNullish } from "@bitwarden/common/auth/utils";
import { PlanSponsorshipType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response";
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
@@ -41,7 +42,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { ToastService } from "@bitwarden/components";
@@ -654,7 +655,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
orgId = this.selfHosted
? await this.createSelfHosted(key, collectionCt, orgKeys)
: await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]);
: await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1], activeUserId);
this.toastService.showToast({
variant: "success",
@@ -808,6 +809,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
collectionCt: string,
orgKeys: [string, EncString],
orgKey: SymmetricCryptoKey,
activeUserId: UserId,
): Promise<string> {
const request = new OrganizationCreateRequest();
request.key = key;
@@ -855,7 +857,14 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
this.formGroup.controls.clientOwnerEmail.value,
request,
);
const providerKey = await this.keyService.getProviderKey(this.providerId);
const providerKey = await firstValueFrom(
this.keyService
.providerKeys$(activeUserId)
.pipe(map((providerKeys) => providerKeys?.[this.providerId as ProviderId] ?? null)),
);
assertNonNullish(providerKey, "Provider key not found");
providerRequest.organizationCreateRequest.key = (
await this.encryptService.wrapSymmetricKey(orgKey, providerKey)
).encryptedString;

View File

@@ -1,8 +1,11 @@
import { Component, Inject, OnInit } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
import { Provider } from "@bitwarden/common/admin-console/models/domain/provider";
import { AddableOrganizationResponse } from "@bitwarden/common/admin-console/models/response/addable-organization.response";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
DIALOG_DATA,
@@ -46,6 +49,7 @@ export class AddExistingOrganizationDialogComponent implements OnInit {
private providerApiService: ProviderApiServiceAbstraction,
private toastService: ToastService,
private webProviderService: WebProviderService,
private accountService: AccountService,
) {}
async ngOnInit() {
@@ -57,9 +61,11 @@ export class AddExistingOrganizationDialogComponent implements OnInit {
addExistingOrganization = async (): Promise<void> => {
if (this.selectedOrganization) {
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
await this.webProviderService.addOrganizationToProvider(
this.dialogParams.provider.id,
this.selectedOrganization.id,
userId,
);
this.toastService.showToast({

View File

@@ -1,6 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, Inject } from "@angular/core";
import { firstValueFrom, map, Observable, switchMap } from "rxjs";
import {
OrganizationUserBulkPublicKeyResponse,
@@ -12,10 +13,14 @@ import { ProviderUserBulkConfirmRequest } from "@bitwarden/common/admin-console/
import { ProviderUserBulkRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-user-bulk.request";
import { ProviderUserBulkPublicKeyResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk-public-key.response";
import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { ProviderId } from "@bitwarden/common/types/guid";
import { ProviderKey } from "@bitwarden/common/types/key";
import { DIALOG_DATA, DialogConfig, DialogService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
import { BaseBulkConfirmComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/base-bulk-confirm.component";
@@ -35,6 +40,7 @@ type BulkConfirmDialogParams = {
})
export class BulkConfirmDialogComponent extends BaseBulkConfirmComponent {
providerId: string;
providerKey$: Observable<ProviderKey>;
constructor(
private apiService: ApiService,
@@ -42,15 +48,21 @@ export class BulkConfirmDialogComponent extends BaseBulkConfirmComponent {
protected encryptService: EncryptService,
@Inject(DIALOG_DATA) protected dialogParams: BulkConfirmDialogParams,
protected i18nService: I18nService,
private accountService: AccountService,
) {
super(keyService, encryptService, i18nService);
this.providerId = dialogParams.providerId;
this.providerKey$ = this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => this.keyService.providerKeys$(userId)),
map((providerKeysById) => providerKeysById?.[this.providerId as ProviderId]),
);
this.users = dialogParams.users;
}
protected getCryptoKey = (): Promise<SymmetricCryptoKey> =>
this.keyService.getProviderKey(this.providerId);
protected getCryptoKey = async (): Promise<SymmetricCryptoKey> =>
await firstValueFrom(this.providerKey$);
protected getPublicKeys = async (): Promise<
ListResponse<OrganizationUserBulkPublicKeyResponse | ProviderUserBulkPublicKeyResponse>

View File

@@ -4,7 +4,7 @@ import { Component } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute, Router } from "@angular/router";
import { combineLatest, firstValueFrom, lastValueFrom, switchMap } from "rxjs";
import { first } from "rxjs/operators";
import { first, map } from "rxjs/operators";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
@@ -16,11 +16,13 @@ import { ProviderUserConfirmRequest } from "@bitwarden/common/admin-console/mode
import { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { assertNonNullish } from "@bitwarden/common/auth/utils";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { ProviderId } from "@bitwarden/common/types/guid";
import { DialogRef, DialogService, ToastService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
import { BaseMembersComponent } from "@bitwarden/web-vault/app/admin-console/common/base-members.component";
@@ -204,7 +206,15 @@ export class MembersComponent extends BaseMembersComponent<ProviderUser> {
async confirmUser(user: ProviderUser, publicKey: Uint8Array): Promise<MemberActionResult> {
try {
const providerKey = await this.keyService.getProviderKey(this.providerId);
const providerKey = await firstValueFrom(
this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => this.keyService.providerKeys$(userId)),
map((providerKeys) => providerKeys?.[this.providerId as ProviderId] ?? null),
),
);
assertNonNullish(providerKey, "Provider key not found");
const key = await this.encryptService.encapsulateKeyUnsigned(providerKey, publicKey);
const request = new ProviderUserConfirmRequest();
request.key = key.encryptedString;

View File

@@ -1,4 +1,5 @@
import { MockProxy, mock } from "jest-mock-extended";
import { of } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
@@ -8,7 +9,6 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { StateProvider } from "@bitwarden/common/platform/state";
import { OrgKey, ProviderKey } from "@bitwarden/common/types/key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { newGuid } from "@bitwarden/guid";
@@ -24,16 +24,22 @@ describe("WebProviderService", () => {
let apiService: MockProxy<ApiService>;
let i18nService: MockProxy<I18nService>;
let encryptService: MockProxy<EncryptService>;
let stateProvider: MockProxy<StateProvider>;
let providerApiService: MockProxy<ProviderApiServiceAbstraction>;
const activeUserId = newGuid() as UserId;
const providerId = "provider-123";
const mockOrgKey = new SymmetricCryptoKey(new Uint8Array(64)) as OrgKey;
const mockProviderKey = new SymmetricCryptoKey(new Uint8Array(64)) as ProviderKey;
const mockProviderKeysById: Record<string, ProviderKey> = {
[providerId]: mockProviderKey,
};
beforeEach(() => {
keyService = mock();
syncService = mock();
apiService = mock();
i18nService = mock();
encryptService = mock();
stateProvider = mock();
providerApiService = mock();
sut = new WebProviderService(
@@ -42,14 +48,69 @@ describe("WebProviderService", () => {
apiService,
i18nService,
encryptService,
stateProvider,
providerApiService,
);
});
describe("addOrganizationToProvider", () => {
const organizationId = "org-789";
const encryptedOrgKey = new EncString("encrypted-org-key");
const mockOrgKeysById: Record<string, OrgKey> = {
[organizationId]: mockOrgKey,
};
beforeEach(() => {
keyService.orgKeys$.mockReturnValue(of(mockOrgKeysById));
keyService.providerKeys$.mockReturnValue(of(mockProviderKeysById));
encryptService.wrapSymmetricKey.mockResolvedValue(encryptedOrgKey);
});
it("adds an organization to a provider with correct encryption", async () => {
await sut.addOrganizationToProvider(providerId, organizationId, activeUserId);
expect(keyService.orgKeys$).toHaveBeenCalledWith(activeUserId);
expect(keyService.providerKeys$).toHaveBeenCalledWith(activeUserId);
expect(encryptService.wrapSymmetricKey).toHaveBeenCalledWith(mockOrgKey, mockProviderKey);
expect(providerApiService.addOrganizationToProvider).toHaveBeenCalledWith(providerId, {
key: encryptedOrgKey.encryptedString,
organizationId,
});
expect(syncService.fullSync).toHaveBeenCalledWith(true);
});
it("throws an error if organization key is not found", async () => {
const invalidOrgId = "invalid-org";
await expect(
sut.addOrganizationToProvider(providerId, invalidOrgId, activeUserId),
).rejects.toThrow("Organization key not found");
});
it("throws an error if no organization keys are available", async () => {
keyService.orgKeys$.mockReturnValue(of(null));
await expect(
sut.addOrganizationToProvider(providerId, organizationId, activeUserId),
).rejects.toThrow("Organization key not found");
});
it("throws an error if provider key is not found", async () => {
const invalidProviderId = "invalid-provider";
await expect(
sut.addOrganizationToProvider(invalidProviderId, organizationId, activeUserId),
).rejects.toThrow("Provider key not found");
});
it("throws an error if no provider keys are available", async () => {
keyService.providerKeys$.mockReturnValue(of(null));
await expect(
sut.addOrganizationToProvider(providerId, organizationId, activeUserId),
).rejects.toThrow("Provider key not found");
});
});
describe("createClientOrganization", () => {
const activeUserId = newGuid() as UserId;
const providerId = "provider-123";
const name = "Test Org";
const ownerEmail = "owner@example.com";
const planType = PlanType.EnterpriseAnnually;
@@ -59,15 +120,13 @@ describe("WebProviderService", () => {
const encryptedProviderKey = new EncString("encrypted-provider-key");
const encryptedCollectionName = new EncString("encrypted-collection-name");
const defaultCollectionTranslation = "Default Collection";
const mockOrgKey = new SymmetricCryptoKey(new Uint8Array(64)) as OrgKey;
const mockProviderKey = new SymmetricCryptoKey(new Uint8Array(64)) as ProviderKey;
beforeEach(() => {
keyService.makeOrgKey.mockResolvedValue([new EncString("mockEncryptedKey"), mockOrgKey]);
keyService.makeKeyPair.mockResolvedValue([publicKey, encryptedPrivateKey]);
i18nService.t.mockReturnValue(defaultCollectionTranslation);
encryptService.encryptString.mockResolvedValue(encryptedCollectionName);
keyService.getProviderKey.mockResolvedValue(mockProviderKey);
keyService.providerKeys$.mockReturnValue(of(mockProviderKeysById));
encryptService.wrapSymmetricKey.mockResolvedValue(encryptedProviderKey);
});
@@ -88,7 +147,7 @@ describe("WebProviderService", () => {
defaultCollectionTranslation,
mockOrgKey,
);
expect(keyService.getProviderKey).toHaveBeenCalledWith(providerId);
expect(keyService.providerKeys$).toHaveBeenCalledWith(activeUserId);
expect(encryptService.wrapSymmetricKey).toHaveBeenCalledWith(mockOrgKey, mockProviderKey);
expect(providerApiService.createProviderOrganization).toHaveBeenCalledWith(
@@ -107,5 +166,27 @@ describe("WebProviderService", () => {
expect(apiService.refreshIdentityToken).toHaveBeenCalled();
expect(syncService.fullSync).toHaveBeenCalledWith(true);
});
it("throws an error if provider key is not found", async () => {
const invalidProviderId = "invalid-provider";
await expect(
sut.createClientOrganization(
invalidProviderId,
name,
ownerEmail,
planType,
seats,
activeUserId,
),
).rejects.toThrow("Provider key not found");
});
it("throws an error if no provider keys are available", async () => {
keyService.providerKeys$.mockReturnValue(of(null));
await expect(
sut.createClientOrganization(providerId, name, ownerEmail, planType, seats, activeUserId),
).rejects.toThrow("Provider key not found");
});
});
});

View File

@@ -1,18 +1,17 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Injectable } from "@angular/core";
import { firstValueFrom, map } from "rxjs";
import { switchMap } from "rxjs/operators";
import { combineLatest, firstValueFrom, map } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
import { CreateProviderOrganizationRequest } from "@bitwarden/common/admin-console/models/request/create-provider-organization.request";
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { assertNonNullish } from "@bitwarden/common/auth/utils";
import { PlanType } from "@bitwarden/common/billing/enums";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { StateProvider } from "@bitwarden/common/platform/state";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { KeyService } from "@bitwarden/key-management";
@@ -25,18 +24,26 @@ export class WebProviderService {
private apiService: ApiService,
private i18nService: I18nService,
private encryptService: EncryptService,
private stateProvider: StateProvider,
private providerApiService: ProviderApiServiceAbstraction,
) {}
async addOrganizationToProvider(providerId: string, organizationId: string): Promise<void> {
const orgKey = await firstValueFrom(
this.stateProvider.activeUserId$.pipe(
switchMap((userId) => this.keyService.orgKeys$(userId)),
map((organizationKeysById) => organizationKeysById[organizationId as OrganizationId]),
),
async addOrganizationToProvider(
providerId: string,
organizationId: string,
activeUserId: UserId,
): Promise<void> {
const [orgKeysById, providerKeys] = await firstValueFrom(
combineLatest([
this.keyService.orgKeys$(activeUserId),
this.keyService.providerKeys$(activeUserId),
]),
);
const providerKey = await this.keyService.getProviderKey(providerId);
const orgKey = orgKeysById?.[organizationId as OrganizationId];
const providerKey = providerKeys?.[providerId as ProviderId];
assertNonNullish(orgKey, "Organization key not found");
assertNonNullish(providerKey, "Provider key not found");
const encryptedOrgKey = await this.encryptService.wrapSymmetricKey(orgKey, providerKey);
await this.providerApiService.addOrganizationToProvider(providerId, {
key: encryptedOrgKey.encryptedString,
@@ -62,7 +69,12 @@ export class WebProviderService {
organizationKey,
);
const providerKey = await this.keyService.getProviderKey(providerId);
const providerKey = await firstValueFrom(
this.keyService
.providerKeys$(activeUserId)
.pipe(map((providerKeys) => providerKeys?.[providerId as ProviderId])),
);
assertNonNullish(providerKey, "Provider key not found");
const encryptedProviderKey = await this.encryptService.wrapSymmetricKey(
organizationKey,

View File

@@ -10,7 +10,7 @@ import {
import { WrappedSigningKey } from "@bitwarden/common/key-management/types";
import { KeySuffixOptions, HashPurpose } from "@bitwarden/common/platform/enums";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid";
import {
UserKey,
MasterKey,
@@ -248,17 +248,19 @@ export abstract class KeyService {
/**
* Stores the provider keys for a given user.
* @param orgs The provider orgs for which to save the keys from.
* @param providers The provider orgs for which to save the keys from.
* @param userId The user id of the user for which to store the keys for.
*/
abstract setProviderKeys(orgs: ProfileProviderResponse[], userId: UserId): Promise<void>;
abstract setProviderKeys(providers: ProfileProviderResponse[], userId: UserId): Promise<void>;
/**
*
* @throws Error when providerId is null or no active user
* @param providerId The desired provider
* @returns The provider's symmetric key
* Gets an observable of provider keys for the given user.
* @param userId The user to get provider keys for.
* @return An observable stream of the users providers keys if they are unlocked, or null if the user is not unlocked.
* @throws If an invalid user id is passed in.
*/
abstract getProviderKey(providerId: string): Promise<ProviderKey | null>;
abstract providerKeys$(userId: UserId): Observable<Record<ProviderId, ProviderKey> | null>;
/**
* Creates a new organization key and encrypts it with the user's public key.
* This method can also return Provider keys for creating new Provider users.

View File

@@ -39,7 +39,7 @@ import {
FakeSingleUserState,
} from "@bitwarden/common/spec";
import { CsprngArray } from "@bitwarden/common/types/csprng";
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { OrganizationId, ProviderId, UserId } from "@bitwarden/common/types/guid";
import {
UserKey,
MasterKey,
@@ -1314,6 +1314,49 @@ describe("keyService", () => {
});
});
describe("providerKeys$", () => {
let mockUserPrivateKey: Uint8Array;
let mockProviderKeys: Record<ProviderId, ProviderKey>;
beforeEach(() => {
mockUserPrivateKey = makeStaticByteArray(64, 1);
mockProviderKeys = {
["provider1" as ProviderId]: makeSymmetricCryptoKey<ProviderKey>(64),
["provider2" as ProviderId]: makeSymmetricCryptoKey<ProviderKey>(64),
};
});
it("returns null when userPrivateKey is null", async () => {
jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(null));
const result = await firstValueFrom(keyService.providerKeys$(mockUserId));
expect(result).toBeNull();
});
it("returns provider keys when userPrivateKey is available", async () => {
jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(mockUserPrivateKey as any));
jest.spyOn(keyService as any, "providerKeysHelper$").mockReturnValue(of(mockProviderKeys));
const result = await firstValueFrom(keyService.providerKeys$(mockUserId));
expect(result).toEqual(mockProviderKeys);
expect((keyService as any).providerKeysHelper$).toHaveBeenCalledWith(
mockUserId,
mockUserPrivateKey,
);
});
it("returns null when providerKeysHelper$ returns null", async () => {
jest.spyOn(keyService, "userPrivateKey$").mockReturnValue(of(mockUserPrivateKey as any));
jest.spyOn(keyService as any, "providerKeysHelper$").mockReturnValue(of(null));
const result = await firstValueFrom(keyService.providerKeys$(mockUserId));
expect(result).toBeNull();
});
});
describe("makeKeyPair", () => {
test.each([null as unknown as SymmetricCryptoKey, undefined as unknown as SymmetricCryptoKey])(
"throws when the provided key is %s",

View File

@@ -426,20 +426,16 @@ export class DefaultKeyService implements KeyServiceAbstraction {
});
}
// TODO: Deprecate in favor of observable
async getProviderKey(providerId: ProviderId): Promise<ProviderKey | null> {
if (providerId == null) {
return null;
}
providerKeys$(userId: UserId): Observable<Record<ProviderId, ProviderKey> | null> {
return this.userPrivateKey$(userId).pipe(
switchMap((userPrivateKey) => {
if (userPrivateKey == null) {
return of(null);
}
const activeUserId = await firstValueFrom(this.stateProvider.activeUserId$);
if (activeUserId == null) {
throw new Error("No active user found.");
}
const providerKeys = await firstValueFrom(this.providerKeys$(activeUserId));
return providerKeys?.[providerId] ?? null;
return this.providerKeysHelper$(userId, userPrivateKey);
}),
);
}
private async clearProviderKeys(userId: UserId): Promise<void> {
@@ -829,18 +825,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
)) as UserPrivateKey;
}
providerKeys$(userId: UserId) {
return this.userPrivateKey$(userId).pipe(
switchMap((userPrivateKey) => {
if (userPrivateKey == null) {
return of(null);
}
return this.providerKeysHelper$(userId, userPrivateKey);
}),
);
}
/**
* A helper for decrypting provider keys that requires a user id and that users decrypted private key
* this is helpful for when you may have already grabbed the user private key and don't want to redo