Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
4d6ba0e
feat(client): add E2EE support via WebRTC Encoded Transforms
oliverlaz Apr 10, 2026
138501a
feat: support more codecs
oliverlaz Apr 10, 2026
86148a3
refactor(client): code-split E2EE into a separate browser chunk
oliverlaz Apr 10, 2026
2b81307
refactor(client): consolidate E2EE into EncryptionManager class
oliverlaz Apr 14, 2026
e32bd0d
fix(client): harden E2EE against review findings
oliverlaz Apr 14, 2026
97d9ff6
feat(client): add E2EE demo app and decryption failure detection
oliverlaz Apr 14, 2026
9a5e9f5
refactor(client): restructure E2EE demo app for clarity and best prac…
oliverlaz Apr 14, 2026
2770b18
fix(e2ee-demo): fix race condition in key exchange on participant join
oliverlaz Apr 14, 2026
a11af82
feat(client): attach E2EE decryptors to orphaned tracks during reconc…
oliverlaz Apr 14, 2026
802e57b
chore: remove accidentally committed worktree pointer
oliverlaz Apr 14, 2026
4d53b5b
chore: co-locate e2eeManager initialization
oliverlaz Apr 14, 2026
79ea956
feat(client): harden E2EE IV derivation, add version byte and perf re…
oliverlaz Apr 15, 2026
283519e
chore: remove accidentally committed worktree pointer
oliverlaz Apr 15, 2026
e4f8189
feat(client): runtime E2EE toggle and worker performance optimizations
oliverlaz Apr 15, 2026
00a0ad0
perf(client): reduce E2EE worker GC pressure and fix silent pipeline …
oliverlaz Apr 15, 2026
7647ec0
perf: speed up encrypting and decrypting of frames
oliverlaz Apr 15, 2026
66213fa
chore: switch to vp8 for better performance
oliverlaz Apr 15, 2026
c487dc1
refactor(client): modularize E2EE worker with build-time TypeScript b…
oliverlaz Apr 16, 2026
6b05723
refactor(client): rename worker to e2ee-worker and export as function
oliverlaz Apr 16, 2026
c4f3589
chore(client): rename inline-worker plugin to .mts to suppress Node E…
oliverlaz Apr 16, 2026
5ef2c9e
feat(client,e2ee-demo): add E2EE usability improvements and debug int…
oliverlaz Apr 16, 2026
f2e762f
feat(client): harden E2EE worker for compliance and performance
oliverlaz Apr 20, 2026
1e9c1b1
feat: use event emitter for information bubbling
oliverlaz Apr 28, 2026
79ae95d
Merge branch 'main' into feat/e2ee-encoded-transforms
oliverlaz Jun 2, 2026
5c19015
chore: adjust to vitest 4
oliverlaz Jun 2, 2026
44fb902
fix(client): scope E2EE replay window per track and surface missing keys
oliverlaz Jun 2, 2026
3fd42a4
chore(e2ee-demo): move event log into each participant box
oliverlaz Jun 2, 2026
3083e4f
fix(client): use VP8 clear-byte count for VP9 in E2EE worker
oliverlaz Jun 3, 2026
88306fd
refactor(client): extract E2EEManager interface for pluggable E2EE
oliverlaz Jun 4, 2026
117ec68
feat(client): add AV1 support to E2EE
oliverlaz Jun 5, 2026
b8148d9
docs(e2ee): add AV1 wire-format design and implementation plan
oliverlaz Jun 5, 2026
f9acbf5
chore(e2ee): remove AV1 design docs from the repo
oliverlaz Jun 5, 2026
99b5595
feat(client): default E2EE to RTCRtpScriptTransform, Insertable Strea…
oliverlaz Jun 5, 2026
646d330
feat(e2ee-demo): bookmarkable call ID and grid/speaker layout switcher
oliverlaz Jun 5, 2026
b85360a
fix(client): bind AV1 E2EE OBUs to frame to close replay/splice gap
oliverlaz Jun 5, 2026
f908a35
refactor(e2ee): rename worker message protocol to cmd.*/e2ee.*
oliverlaz Jun 8, 2026
9019c49
refactor(e2ee): deliver key dumps via the e2ee.key_state event
oliverlaz Jun 8, 2026
c90163e
fix(e2ee): type-check the worker with its own WebWorker tsconfig
oliverlaz Jun 8, 2026
3f25784
feat(e2ee-demo): redesign sample app into an E2EE dev/QA harness
oliverlaz Jun 9, 2026
09a8d95
chore: ensure trudy doesn't get any encryption keys
oliverlaz Jun 9, 2026
20f770d
feat(e2ee): default Chrome to Insertable Streams, add preferredTransf…
oliverlaz Jun 10, 2026
93aa7a4
feat(e2ee-demo): feature-detect and preselect the transform path
oliverlaz Jun 10, 2026
0d9b4a5
docs(e2ee): correct setKey userId param
oliverlaz Jun 10, 2026
91aec91
fix(e2ee): authenticate frames before mutating replay/failure state
oliverlaz Jun 10, 2026
714a904
fix(e2ee): validate keyIndex range and RBSP-escape the H264 trailer
oliverlaz Jun 10, 2026
4e1c516
fix(e2ee): guard late setE2EEManager, fail closed on unsupported code…
oliverlaz Jun 10, 2026
d21975a
fix(e2ee): harden key handling, event unsubscribe, and clear-byte clamp
oliverlaz Jun 10, 2026
3a9d896
perf(e2ee): O(1) replay-window bitmap and hoist the AV1 inline header
oliverlaz Jun 10, 2026
b587c8c
refactor(e2ee): collapse parallel key maps and dedupe worker helpers
oliverlaz Jun 10, 2026
b16f0a6
Merge branch 'refs/heads/main' into feat/e2ee-encoded-transforms
oliverlaz Jun 11, 2026
966eb9b
perf(e2ee): single-copy H264 escape and AV1 inline-header prefix
oliverlaz Jun 12, 2026
3f02b77
refactor(e2ee): simplify emitter, worker-event contract, codec/key di…
oliverlaz Jun 12, 2026
1bdeeef
fix(e2ee): per-track decryption-failure accounting and a re-armable e…
oliverlaz Jun 12, 2026
41497db
refactor(e2ee): replace the worker task queue with a 4-line promise t…
oliverlaz Jun 12, 2026
62e136c
chore(e2ee): drop "finding N" review-doc cross-references from code a…
oliverlaz Jun 12, 2026
75acbae
chore(e2ee): number the trailer wire format v1
oliverlaz Jun 12, 2026
4b53e3e
refactor(e2ee): drop dead trailer version field and test-only rbspEscape
oliverlaz Jun 12, 2026
e1a0fbb
Merge branch 'main' into feat/e2ee-encoded-transforms
oliverlaz Jun 30, 2026
3d994db
chore: align ts-config
oliverlaz Jun 30, 2026
c435d2c
Merge branch 'main' into feat/e2ee-encoded-transforms
oliverlaz Jul 14, 2026
348aba8
chore: upgrade to the latest openapi schema
oliverlaz Jul 14, 2026
9e5c44d
feat(client): declare e2ee intent on join when a manager is attached
oliverlaz Jul 14, 2026
3f8334b
chore(e2ee-demo): create encrypted calls and add environment override
oliverlaz Jul 14, 2026
2a84885
chore(e2ee-demo): reframe spy labels as admitted-but-keyless
oliverlaz Jul 14, 2026
9f642e6
ci(e2ee-demo): add Vercel deployment job
oliverlaz Jul 14, 2026
a1f0e51
chore: upgrade protocol
oliverlaz Jul 15, 2026
1a2ad69
chore(e2ee-demo): default to pronto-staging environment
oliverlaz Jul 15, 2026
06f67cd
chore(e2ee-demo): make E2EE always-on and unlock shared-key setup
oliverlaz Jul 15, 2026
8a3be9d
feat(e2ee-demo): manual per-participant key override for multi-tab te…
oliverlaz Jul 15, 2026
f470842
fix(e2ee-demo): use a fixed shared-key index for cross-tab decryption
oliverlaz Jul 15, 2026
b459e94
feat(client): report E2EE encode/decode perf per track
oliverlaz Jul 15, 2026
02f151e
chore(e2ee-demo): show per-track E2EE perf, stably sorted
oliverlaz Jul 15, 2026
4b5320a
feat(react-dogfood): optional end-to-end encryption (pronto)
oliverlaz Jul 16, 2026
8de2d9a
refactor(react-dogfood): seamless E2EE lobby toggle
oliverlaz Jul 16, 2026
1577bdb
refactor(react-dogfood): extract call lifecycle + E2EE glue from join…
oliverlaz Jul 16, 2026
4cb556e
feat(react-dogfood): restrict E2EE toggle to the call creator
oliverlaz Jul 16, 2026
960a064
chore(react-dogfood): tidy redundant comment in LobbyEncryption
oliverlaz Jul 16, 2026
8a0b904
chore: exclude `e2ee` from JoinCallData
oliverlaz Jul 16, 2026
e4973d1
chore: silence verbose exhaust checks
oliverlaz Jul 16, 2026
cb60c34
refactor(client): tidy E2EE manager API surface
oliverlaz Jul 16, 2026
fcdfae5
feat(react-dogfood): add --e2ee/--private to the Slack create hook
oliverlaz Jul 17, 2026
ac7c617
feat(client): expose the SFU e2ee_enabled flag as call state
oliverlaz Jul 27, 2026
b08dc4b
chore(e2ee-demo): create calls with the auto-on encryption mode
oliverlaz Jul 27, 2026
decfce9
feat(e2ee-demo): add a call type override and console debug handles
oliverlaz Jul 28, 2026
0736ee2
feat(e2ee-demo): let the encryption mode be picked before call creation
oliverlaz Jul 28, 2026
ae638d7
feat(e2ee-demo): take the encryption mode from the backend
oliverlaz Jul 28, 2026
ea87c03
fix(e2ee-demo): retire a participant panel once its call is left
oliverlaz Jul 28, 2026
df40af6
refactor(client): drop shouldUseInsertableStreams from the E2EEManage…
oliverlaz Jul 28, 2026
480abbc
fix(e2ee-demo): stop offering key revocation to peers that hold no key
oliverlaz Jul 28, 2026
b24d1c1
fix(client): harden the E2EE decode path and disposed-manager handling
oliverlaz Jul 29, 2026
382a7ce
feat(client): report the failing track type on E2EE decryption events
oliverlaz Jul 29, 2026
9035899
refactor(client): drop AV1 from the E2EE worker
oliverlaz Jul 29, 2026
abb8673
chore: simplifications
oliverlaz Jul 29, 2026
b198fa4
refactor(client): drop the E2EE rotation_needed event
oliverlaz Jul 30, 2026
2d08713
fix(client): pair the E2EE decryption failure and recovery signals
oliverlaz Jul 30, 2026
75d3512
docs(client): trim the E2EE comments
oliverlaz Jul 30, 2026
7f0a2df
refactor(client): change the E2EE trailer magic to 0xE2EEFEED
oliverlaz Jul 30, 2026
5a41618
fix(client): fail closed on a video frame with no codec
oliverlaz Jul 30, 2026
d953832
refactor(client): split the E2EE worker into per-concern modules
oliverlaz Jul 30, 2026
ce5c577
fix(client): close the H.264 RBSP escaping gap at the clear-header bo…
oliverlaz Jul 30, 2026
200d48d
test(client): consolidate the E2EE suite and cover the untested modules
oliverlaz Jul 30, 2026
59951da
test(client): fold the repetitive E2EE tests into tables
oliverlaz Jul 30, 2026
58dc3ca
Merge remote-tracking branch 'origin/main' into feat/e2ee-encoded-tra…
oliverlaz Jul 30, 2026
b89a72b
refactor(client): turn the E2EE worker factories into classes
oliverlaz Jul 30, 2026
c340357
feat(client): support shared E2EE key rotation
oliverlaz Jul 30, 2026
86e1a1a
refactor(client): use arrow fields in typed event emitter
oliverlaz Jul 30, 2026
fdc77cb
refactor(client): split the E2EE worker crypto module
oliverlaz Jul 31, 2026
0cdfa1b
docs(client): add the E2EE cross-SDK implementation spec
oliverlaz Jul 31, 2026
3aea0a7
refactor(client): let TypedEventEmitter reuse a provided logger
oliverlaz Aug 3, 2026
4a9dc63
docs(client): drop the E2EE worker module headers
oliverlaz Aug 3, 2026
66917eb
Merge branch 'main' into feat/e2ee-encoded-transforms
oliverlaz Aug 3, 2026
a811676
Merge remote-tracking branch 'origin/main' into feat/e2ee-encoded-tra…
oliverlaz Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/deploy-react-sample-apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ jobs:
project-id: prj_uNJTw7DefSAntAoWCXwJaHc1khoA
- name: audio-rooms
project-id: prj_0WnHcvVkXpM4PRc2ymVmrAHFILoT
- name: e2ee-demo - https://stream-e2ee-demo.vercel.app
project-id: prj_fdQ1cP6fTIyXkjmoKfLhNnzANF4M
- name: react-dogfood - https://pronto.getstream.io
project-id: prj_4TTdjeVHEDhWWiFRfjIr1QFb5ell
- name: react-dogfood-staging - https://pronto-staging.getstream.io
Expand Down
2 changes: 2 additions & 0 deletions packages/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export * from './src/helpers/DynascaleManager';
export * from './src/helpers/ViewportTracker';
export * from './src/helpers/sound-detector';
export * from './src/helpers/participantUtils';
export * from './src/rtc/e2ee/E2EEManager';
export * from './src/rtc/e2ee/EncryptionManager';
export * as Browsers from './src/helpers/browsers';

export * from './src/logger';
68 changes: 68 additions & 0 deletions packages/client/plugins/rollup-plugin-inline-worker.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { rollup, type Plugin } from 'rollup';
import typescript from '@rollup/plugin-typescript';
import { format, resolveConfig } from 'prettier';
import { dirname, resolve } from 'path';

interface InlineWorkerOptions {
/** File names (not paths) that trigger the plugin, e.g. `['worker.ts']`. */
include: string[];
}

/**
* Rollup plugin that bundles worker TypeScript files into inline functions.
*
* Only files whose path ends with one of the provided `include` patterns
* are processed β€” all other modules are skipped with zero overhead.
*
* For each matched file, the plugin:
* 1. Finds the corresponding `-impl.ts` entry (e.g. `worker.ts` β†’ `worker/worker-impl.ts`)
* 2. Bundles it with a nested Rollup + TypeScript build
* 3. Wraps the result in an exported function and formats with prettier
*
* The consumer creates a Worker from the function via:
* `new Worker(\`data:text/javascript,(\${fn.toString()})()\`)`
* or via a Blob URL.
*/
export default function inlineWorker({ include }: InlineWorkerOptions): Plugin {
const fileNames = new Set(include);

return {
name: 'inline-worker',

async load(id: string) {
const fileName = id.split('/').pop();
if (!fileNames.has(fileName!)) return null;

// e2ee-worker.ts β†’ e2ee-worker/e2ee-worker-impl.ts
const dir = dirname(id);
const name = fileName!.replace(/\.ts$/, '');
const implPath = resolve(dir, name, `${name}-impl.ts`);

const bundle = await rollup({
input: implPath,
plugins: [
typescript({
tsconfig: resolve(dir, name, 'tsconfig.json'),
exclude: ['**/node_modules/**', '**/__tests__/**'],
}),
],
});

const { output } = await bundle.generate({
format: 'es',
indent: false,
sourcemap: false,
});
await bundle.close();

// Wrap bundled code in an exported function, then format with prettier.
return await format(
`export function e2eeWorker() { ${output[0].code} }`,
{
parser: 'babel',
...(await resolveConfig(implPath)),
},
);
},
};
}
7 changes: 6 additions & 1 deletion packages/client/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import typescript from '@rollup/plugin-typescript';
import replace from '@rollup/plugin-replace';
import inlineWorker from './plugins/rollup-plugin-inline-worker.mts';

import pkg from './package.json' with { type: 'json' };

Expand Down Expand Up @@ -30,12 +31,15 @@ const external = [
const browserConfig = {
input: 'index.ts',
output: {
file: 'dist/index.browser.es.js',
dir: 'dist',
format: 'esm',
sourcemap: true,
entryFileNames: 'index.browser.es.js',
chunkFileNames: '[name].browser.es.js',
},
external: external.filter((dep) => !browserIgnoredModules.includes(dep)),
plugins: [
inlineWorker({ include: ['e2ee-worker.ts'] }),
replace({
preventAssignment: true,
'process.env.PKG_VERSION': JSON.stringify(pkg.version),
Expand All @@ -55,6 +59,7 @@ const createNodeConfig = (outputFile, format) => ({
file: outputFile,
format: format,
sourcemap: true,
inlineDynamicImports: true,
},
external,
plugins: [
Expand Down
65 changes: 52 additions & 13 deletions packages/client/src/Call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
TrackPublishOptions,
trackTypeToParticipantStreamKey,
} from './rtc';
import type { E2EEManager } from './rtc/e2ee/E2EEManager';
import {
registerEventHandlers,
registerRingingCallEventHandlers,
Expand All @@ -30,7 +31,7 @@ import {
getCurrentValue,
} from './store/rxUtils';
import { ScopedLogger, videoLoggerSystem } from './logger';
import type {
import {
AcceptCallResponse,
BlockUserRequest,
BlockUserResponse,
Expand Down Expand Up @@ -76,8 +77,8 @@ import type {
RingCallResponse,
SendCallEventRequest,
SendCallEventResponse,
SendReactionRequest,
SendReactionResponse,
SendVideoReactionRequest,
SendVideoReactionResponse,
StartClosedCaptionsRequest,
StartClosedCaptionsResponse,
StartFrameRecordingRequest,
Expand Down Expand Up @@ -109,6 +110,8 @@ import type {
UpdateCallRequest,
UpdateCallResponse,
UpdateUserPermissionsRequest,
UpdateUserPermissionsRequestGrantPermissionsEnum,
UpdateUserPermissionsRequestRevokePermissionsEnum,
UpdateUserPermissionsResponse,
} from './gen/coordinator';
import { OwnCapability } from './gen/coordinator';
Expand Down Expand Up @@ -266,6 +269,7 @@ export class Call {

subscriber?: Subscriber;
publisher?: Publisher;
e2eeManager?: E2EEManager;

/**
* Flag telling whether this call is a "ringing" call.
Expand Down Expand Up @@ -1542,6 +1546,7 @@ export class Call {
this.sfuStatsReporter = undefined;
if (closePreviousInstances && this.subscriber) {
await this.subscriber.dispose();
this.state.removeAllOrphanedTracks();
}
const basePeerConnectionOptions: BasePeerConnectionOpts = {
sfuClient,
Expand All @@ -1552,6 +1557,7 @@ export class Call {
enableTracing,
statsTimestampDriftThresholdMs: reportingIntervalMs / 2,
clientPublishOptions: this.clientPublishOptions,
e2ee: this.e2eeManager,
onReconnectionNeeded: (kind, reason, peerType) => {
this.reconnect(kind, reason).catch((err) => {
const message = `[Reconnect] Error reconnecting, after a ${PeerType[peerType]} error: ${reason}`;
Expand Down Expand Up @@ -1628,12 +1634,12 @@ export class Call {
* Retrieves credentials for joining the call.
*
* @internal
*
* @param data the join call data.
*/
doJoinRequest = async (data?: JoinCallData): Promise<JoinCallResponse> => {
const location = await this.streamClient.getLocationHint();
const request: JoinCallRequest = { ...data, location };
const e2ee = !!this.e2eeManager;
const request: JoinCallRequest = { ...data, location, e2ee };
const joinResponse = await this.streamClient.post<
JoinCallResponse,
JoinCallRequest
Expand Down Expand Up @@ -2355,6 +2361,31 @@ export class Call {
this.clientPublishOptions = { ...this.clientPublishOptions, ...options };
};

/**
* Set the E2EE (end-to-end encryption) manager for this call.
*
* Must be called before {@link join} so the RTCPeerConnection can be
* configured for E2EE.
*
* The manager is kept across {@link leave} so a rejoin of this same instance
* stays encrypted: do not dispose it while this call may be joined again.
* A disposed manager throws from `encrypt`/`decrypt` rather than silently
* publishing nothing, so re-attach a fresh one instead of reusing it.
*
* @param e2ee - Any `E2EEManager`. Use `EncryptionManager.create()` for the
* built-in AES-GCM scheme, or pass your own implementation.
* @throws if called after the peer connections have been built (i.e. after
* `join`): those PCs were already configured without an encryptor, so
* adopting a manager now would silently publish/receive cleartext for
* the live session.
*/
setE2EEManager = (e2ee: E2EEManager) => {
if (this.publisher || this.subscriber) {
throw new Error('setE2EEManager must be called before join()');
}
this.e2eeManager = e2ee;
};

/**
* Notifies the SFU that a noise cancellation process has started.
*
Expand Down Expand Up @@ -2453,9 +2484,9 @@ export class Call {
* @param reaction the reaction to send.
*/
sendReaction = async (
reaction: SendReactionRequest,
): Promise<SendReactionResponse> => {
return this.streamClient.post<SendReactionResponse, SendReactionRequest>(
reaction: SendVideoReactionRequest,
): Promise<SendVideoReactionResponse> => {
return this.streamClient.post(
`${this.streamClientBasePath}/reaction`,
reaction,
);
Expand Down Expand Up @@ -2673,7 +2704,7 @@ export class Call {
): Promise<RequestPermissionResponse> => {
const { permissions } = data;
const canRequestPermissions = permissions.every((permission) =>
this.permissionsContext.canRequest(permission as OwnCapability),
this.permissionsContext.canRequest(permission),
);
if (!canRequestPermissions) {
throw new Error(
Expand All @@ -2698,10 +2729,14 @@ export class Call {
* @param userId the id of the user to grant permissions to.
* @param permissions the permissions to grant.
*/
grantPermissions = async (userId: string, permissions: string[]) => {
grantPermissions = async (
userId: string,
permissions: string[] | UpdateUserPermissionsRequestGrantPermissionsEnum[],
) => {
return this.updateUserPermissions({
user_id: userId,
grant_permissions: permissions,
grant_permissions:
permissions as UpdateUserPermissionsRequestGrantPermissionsEnum[],
});
};

Expand All @@ -2717,10 +2752,14 @@ export class Call {
* @param userId the id of the user to revoke permissions from.
* @param permissions the permissions to revoke.
*/
revokePermissions = async (userId: string, permissions: string[]) => {
revokePermissions = async (
userId: string,
permissions: string[] | UpdateUserPermissionsRequestRevokePermissionsEnum[],
) => {
return this.updateUserPermissions({
user_id: userId,
revoke_permissions: permissions,
revoke_permissions:
permissions as UpdateUserPermissionsRequestRevokePermissionsEnum[],
});
};

Expand Down
15 changes: 15 additions & 0 deletions packages/client/src/__tests__/Call.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Dispatcher } from '../rtc';
import { Call } from '../Call';
import { StreamVideoParticipant } from '../types';
import { TrackType } from '../gen/video/sfu/models/models';
import type { E2EEManager } from '../rtc/e2ee/E2EEManager';

const apiKey = process.env.STREAM_API_KEY!;
const secret = process.env.STREAM_SECRET!;
Expand Down Expand Up @@ -300,6 +301,20 @@ describe('muting logic', () => {
});
});

describe('setE2EEManager', () => {
it('throws when called after the peer connections exist', () => {
const call = client.call('default', generateUUIDv4());
const e2ee = fromPartial<E2EEManager>({});
// Before join there are no peer connections, so it is accepted.
expect(() => call.setE2EEManager(e2ee)).not.toThrow();
// Once a subscriber/publisher exists (i.e. after join) the PCs were already
// built without the manager; setting it now would leave the live session
// half-encrypted, so it must be rejected (finding 12).
call['subscriber'] = fromPartial({});
expect(() => call.setE2EEManager(e2ee)).toThrow(/before join/);
});
});

describe('client event reporting', () => {
afterEach(() => {
vi.restoreAllMocks();
Expand Down
9 changes: 6 additions & 3 deletions packages/client/src/coordinator/connection/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import {
} from './utils';
import type { StreamVideoEvent, UR, WSConnectionError } from './types';
import type { LogLevel } from '@stream-io/logger';
import type {
import {
ConnectedEvent,
ConnectionErrorEvent,
WSAuthMessage,
WSAuthMessageProductsEnum,
} from '../../gen/coordinator';
import { makeSafePromise, type SafePromise } from '../../helpers/promise';
import { getTimers } from '../../timers';
Expand Down Expand Up @@ -529,15 +530,17 @@ export class StableWSConnection {
return;
}

const authMessage = JSON.stringify({
const wsAuthMessage: WSAuthMessage = {
token,
products: [WSAuthMessageProductsEnum.VIDEO],
user_details: {
id: user.id,
name: user.name,
image: user.image,
custom: user.custom,
},
} as WSAuthMessage);
};
const authMessage = JSON.stringify(wsAuthMessage);

this._log(`onopen() - Sending auth message ${authMessage}`, {}, 'trace');

Expand Down
Loading
Loading