1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00

[PM-23189] Add client managed token provider (#15408)

* Add client managed token provider

* Change token service to accept user id

* Resolve breaking changes in the SDK

* Fix tests

* Update sdk

* Fix type

* Fix types

* Fix cli

* Fix browser

* Add optional userId to refreshIdentityToken

* Fix merge issues

* Fix tests
This commit is contained in:
Oscar Hinton
2025-10-08 22:47:30 +02:00
committed by GitHub
parent 5f18f15c52
commit 095729d6fa
6 changed files with 26 additions and 7 deletions

View File

@@ -830,6 +830,7 @@ export default class MainBackground {
this.accountService, this.accountService,
this.kdfConfigService, this.kdfConfigService,
this.keyService, this.keyService,
this.apiService,
this.stateProvider, this.stateProvider,
this.configService, this.configService,
); );

View File

@@ -612,6 +612,7 @@ export class ServiceContainer {
this.accountService, this.accountService,
this.kdfConfigService, this.kdfConfigService,
this.keyService, this.keyService,
this.apiService,
this.stateProvider, this.stateProvider,
this.configService, this.configService,
customUserAgent, customUserAgent,

View File

@@ -1523,6 +1523,7 @@ const safeProviders: SafeProvider[] = [
AccountServiceAbstraction, AccountServiceAbstraction,
KdfConfigService, KdfConfigService,
KeyService, KeyService,
ApiServiceAbstraction,
StateProvider, StateProvider,
ConfigService, ConfigService,
], ],

View File

@@ -163,7 +163,7 @@ export abstract class ApiService {
): Promise< ): Promise<
IdentityTokenResponse | IdentityTwoFactorResponse | IdentityDeviceVerificationResponse IdentityTokenResponse | IdentityTwoFactorResponse | IdentityDeviceVerificationResponse
>; >;
abstract refreshIdentityToken(): Promise<any>; abstract refreshIdentityToken(userId?: UserId): Promise<any>;
abstract getProfile(): Promise<ProfileResponse>; abstract getProfile(): Promise<ProfileResponse>;
abstract getUserSubscription(): Promise<SubscriptionResponse>; abstract getUserSubscription(): Promise<SubscriptionResponse>;

View File

@@ -8,11 +8,12 @@ import { KdfConfigService, KeyService, PBKDF2KdfConfig } from "@bitwarden/key-ma
import { BitwardenClient } from "@bitwarden/sdk-internal"; import { BitwardenClient } from "@bitwarden/sdk-internal";
import { import {
ObservableTracker,
FakeAccountService, FakeAccountService,
FakeStateProvider, FakeStateProvider,
mockAccountServiceWith, mockAccountServiceWith,
ObservableTracker,
} from "../../../../spec"; } from "../../../../spec";
import { ApiService } from "../../../abstractions/api.service";
import { AccountInfo } from "../../../auth/abstractions/account.service"; import { AccountInfo } from "../../../auth/abstractions/account.service";
import { EncryptedString } from "../../../key-management/crypto/models/enc-string"; import { EncryptedString } from "../../../key-management/crypto/models/enc-string";
import { UserId } from "../../../types/guid"; import { UserId } from "../../../types/guid";
@@ -46,6 +47,7 @@ describe("DefaultSdkService", () => {
let service!: DefaultSdkService; let service!: DefaultSdkService;
let accountService!: FakeAccountService; let accountService!: FakeAccountService;
let fakeStateProvider!: FakeStateProvider; let fakeStateProvider!: FakeStateProvider;
let apiService!: MockProxy<ApiService>;
beforeEach(async () => { beforeEach(async () => {
await new TestSdkLoadService().loadAndInit(); await new TestSdkLoadService().loadAndInit();
@@ -55,6 +57,7 @@ describe("DefaultSdkService", () => {
platformUtilsService = mock<PlatformUtilsService>(); platformUtilsService = mock<PlatformUtilsService>();
kdfConfigService = mock<KdfConfigService>(); kdfConfigService = mock<KdfConfigService>();
keyService = mock<KeyService>(); keyService = mock<KeyService>();
apiService = mock<ApiService>();
const mockUserId = Utils.newGuid() as UserId; const mockUserId = Utils.newGuid() as UserId;
accountService = mockAccountServiceWith(mockUserId); accountService = mockAccountServiceWith(mockUserId);
fakeStateProvider = new FakeStateProvider(accountService); fakeStateProvider = new FakeStateProvider(accountService);
@@ -72,6 +75,7 @@ describe("DefaultSdkService", () => {
accountService, accountService,
kdfConfigService, kdfConfigService,
keyService, keyService,
apiService,
fakeStateProvider, fakeStateProvider,
configService, configService,
); );

View File

@@ -27,6 +27,7 @@ import {
UnsignedSharedKey, UnsignedSharedKey,
} from "@bitwarden/sdk-internal"; } from "@bitwarden/sdk-internal";
import { ApiService } from "../../../abstractions/api.service";
import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service"; import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service";
import { DeviceType } from "../../../enums/device-type.enum"; import { DeviceType } from "../../../enums/device-type.enum";
import { EncryptedString, EncString } from "../../../key-management/crypto/models/enc-string"; import { EncryptedString, EncString } from "../../../key-management/crypto/models/enc-string";
@@ -43,7 +44,7 @@ import { StateProvider } from "../../state";
import { initializeState } from "./client-managed-state"; import { initializeState } from "./client-managed-state";
// A symbol that represents an overriden client that is explicitly set to undefined, // A symbol that represents an overridden client that is explicitly set to undefined,
// blocking the creation of an internal client for that user. // blocking the creation of an internal client for that user.
const UnsetClient = Symbol("UnsetClient"); const UnsetClient = Symbol("UnsetClient");
@@ -51,10 +52,17 @@ const UnsetClient = Symbol("UnsetClient");
* A token provider that exposes the access token to the SDK. * A token provider that exposes the access token to the SDK.
*/ */
class JsTokenProvider implements TokenProvider { class JsTokenProvider implements TokenProvider {
constructor() {} constructor(
private apiService: ApiService,
private userId?: UserId,
) {}
async get_access_token(): Promise<string | undefined> { async get_access_token(): Promise<string | undefined> {
return undefined; if (this.userId == null) {
return undefined;
}
return await this.apiService.getActiveBearerToken(this.userId);
} }
} }
@@ -68,7 +76,10 @@ export class DefaultSdkService implements SdkService {
concatMap(async (env) => { concatMap(async (env) => {
await SdkLoadService.Ready; await SdkLoadService.Ready;
const settings = this.toSettings(env); const settings = this.toSettings(env);
const client = await this.sdkClientFactory.createSdkClient(new JsTokenProvider(), settings); const client = await this.sdkClientFactory.createSdkClient(
new JsTokenProvider(this.apiService),
settings,
);
await this.loadFeatureFlags(client); await this.loadFeatureFlags(client);
return client; return client;
}), }),
@@ -87,6 +98,7 @@ export class DefaultSdkService implements SdkService {
private accountService: AccountService, private accountService: AccountService,
private kdfConfigService: KdfConfigService, private kdfConfigService: KdfConfigService,
private keyService: KeyService, private keyService: KeyService,
private apiService: ApiService,
private stateProvider: StateProvider, private stateProvider: StateProvider,
private configService: ConfigService, private configService: ConfigService,
private userAgent: string | null = null, private userAgent: string | null = null,
@@ -173,7 +185,7 @@ export class DefaultSdkService implements SdkService {
const settings = this.toSettings(env); const settings = this.toSettings(env);
const client = await this.sdkClientFactory.createSdkClient( const client = await this.sdkClientFactory.createSdkClient(
new JsTokenProvider(), new JsTokenProvider(this.apiService, userId),
settings, settings,
); );