Skip to content

Commit 12749ae

Browse files
authored
fix: Allow guest and anonymous users without auth options (#2140)
Update StreamVideoClientOptions typing so guest and anonymous users must not provide token or tokenProvider, while authenticated users must provide at least one of them. Continuation of: #2138 ### 📝 Implementation notes - disallow token/tokenProvider for guest and anonymous user options - keep authenticated users requiring token or tokenProvider - keep token/tokenProvider disallowed when user is omitted - verified with package and sample app TypeScript checks 🎫 Ticket: https://linear.app/stream/issue/RN-352/error-in-streamvideoclientgetorcreateinstance-method-and-user 📑 Docs: GetStream/docs-content#1042 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Enhanced type safety with distinct configuration options for guest, anonymous, and authenticated users. * **Bug Fixes** * Simplified authentication flow for guest and anonymous users, reducing unnecessary token/provider configurations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 2111607 commit 12749ae

File tree

5 files changed

+85
-59
lines changed

5 files changed

+85
-59
lines changed

packages/client/src/types.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -350,16 +350,28 @@ type StreamVideoClientOptionsWithoutUser = StreamVideoClientBaseOptions & {
350350
tokenProvider?: never;
351351
};
352352

353-
type StreamVideoClientOptionsWithUser = StreamVideoClientBaseOptions & {
354-
user: User;
355-
} & (
356-
| { token: string; tokenProvider?: TokenProvider }
357-
| { token?: string; tokenProvider: TokenProvider }
358-
);
353+
type GuestOrAnonymousUser = Extract<User, { type: 'guest' | 'anonymous' }>;
354+
type AuthenticatedUser = Exclude<User, GuestOrAnonymousUser>;
355+
356+
type StreamVideoClientOptionsWithGuestOrAnonymousUser =
357+
StreamVideoClientBaseOptions & {
358+
user: GuestOrAnonymousUser;
359+
token?: never;
360+
tokenProvider?: never;
361+
};
362+
363+
type StreamVideoClientOptionsWithAuthenticatedUser =
364+
StreamVideoClientBaseOptions & {
365+
user: AuthenticatedUser;
366+
} & (
367+
| { token: string; tokenProvider?: TokenProvider }
368+
| { token?: string; tokenProvider: TokenProvider }
369+
);
359370

360371
export type StreamVideoClientOptions =
361372
| StreamVideoClientOptionsWithoutUser
362-
| StreamVideoClientOptionsWithUser;
373+
| StreamVideoClientOptionsWithGuestOrAnonymousUser
374+
| StreamVideoClientOptionsWithAuthenticatedUser;
363375

364376
export type CallRecordingType = CallRecordingStartedEventRecordingTypeEnum;
365377
export type StartCallRecordingFnType = {

sample-apps/react-native/dogfood/src/screens/Meeting/GuestMeetingScreen.tsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,16 @@ export const GuestMeetingScreen = (props: Props) => {
4545
useEffect(() => {
4646
let _videoClient: StreamVideoClient | undefined;
4747
const run = async () => {
48-
const fetchAuthDetails = async () => {
49-
return await createToken(
50-
{
51-
user_id: userToConnect.id ?? '!anon',
52-
call_cids:
53-
mode === 'anonymous' ? `${callType}:${callId}` : undefined,
54-
},
55-
appEnvironment,
56-
);
57-
};
58-
const { apiKey } = await fetchAuthDetails();
59-
const tokenProvider = () => fetchAuthDetails().then((auth) => auth.token);
48+
const { apiKey } = await createToken(
49+
{
50+
user_id: userToConnect.id ?? '!anon',
51+
call_cids: mode === 'anonymous' ? `${callType}:${callId}` : undefined,
52+
},
53+
appEnvironment,
54+
);
6055
_videoClient = StreamVideoClient.getOrCreateInstance({
6156
apiKey,
6257
user: userToConnect,
63-
tokenProvider,
6458
options: { logLevel: 'warn', rejectCallWhenBusy: true },
6559
});
6660
setVideoClient(_videoClient);

sample-apps/react-native/expo-video-sample/components/VideoWrapper.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,31 @@ export const VideoWrapper = ({ children }: PropsWithChildren<{}>) => {
1515
useEffect(() => {
1616
let _videoClient: StreamVideoClient | undefined;
1717
const run = async () => {
18-
const user_id = user?.id;
18+
if (!user) return;
19+
const user_id = user.type === 'anonymous' ? '!anon' : user.id;
1920
if (!user_id) return;
2021
const fetchAuthDetails = async () => {
2122
return await createToken({ user_id });
2223
};
2324
const { apiKey, token } = await fetchAuthDetails();
24-
const tokenProvider = () => fetchAuthDetails().then((auth) => auth.token);
25-
_videoClient = StreamVideoClient.getOrCreateInstance({
26-
apiKey,
27-
user,
28-
token,
29-
tokenProvider,
30-
options: { logLevel: 'warn' },
31-
});
25+
26+
if (user.type === 'guest' || user.type === 'anonymous') {
27+
_videoClient = StreamVideoClient.getOrCreateInstance({
28+
apiKey,
29+
user,
30+
options: { logLevel: 'warn' },
31+
});
32+
} else {
33+
const tokenProvider = () =>
34+
fetchAuthDetails().then((auth) => auth.token);
35+
_videoClient = StreamVideoClient.getOrCreateInstance({
36+
apiKey,
37+
user,
38+
token,
39+
tokenProvider,
40+
options: { logLevel: 'warn' },
41+
});
42+
}
3243
setVideoClient(_videoClient);
3344
};
3445
run();

sample-apps/react/livestream-app/src/hooks/useInitVideoClient.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const useInitVideoClient = ({
2121
const { callId } = useParams<{ callId: string }>();
2222
const { api_key, token, type } = getURLCredentials();
2323
const user = useMemo<User>(() => {
24-
return isAnon ? { id: '!anon', type: 'anonymous' } : getUser();
24+
return isAnon ? { type: 'anonymous' } : getUser();
2525
}, [isAnon]);
2626
const apiKey = api_key ?? envApiKey;
2727

@@ -31,9 +31,12 @@ export const useInitVideoClient = ({
3131
const tokenProvider = async () => {
3232
const endpoint = new URL(tokenProviderUrl);
3333
endpoint.searchParams.set('api_key', apiKey);
34-
endpoint.searchParams.set('user_id', isAnon ? '!anon' : user.id!);
34+
endpoint.searchParams.set(
35+
'user_id',
36+
user.type === 'anonymous' ? '!anon' : user.id!,
37+
);
3538

36-
if (isAnon) {
39+
if (user.type === 'anonymous') {
3740
endpoint.searchParams.set(
3841
'call_cids',
3942
`${type ?? DEFAULT_CALL_TYPE}:${callId}`,
@@ -42,12 +45,14 @@ export const useInitVideoClient = ({
4245
const response = await fetch(endpoint).then((res) => res.json());
4346
return response.token as string;
4447
};
45-
const _client = new StreamVideoClient({
46-
apiKey,
47-
user,
48-
token,
49-
tokenProvider,
50-
});
48+
const _client =
49+
user.type === 'anonymous' || user.type === 'guest'
50+
? new StreamVideoClient({ apiKey, user })
51+
: new StreamVideoClient({
52+
apiKey,
53+
user,
54+
...(token ? { token, tokenProvider } : { tokenProvider }),
55+
});
5156
setClient(_client);
5257

5358
return () => {
@@ -56,7 +61,7 @@ export const useInitVideoClient = ({
5661
.catch((error) => console.error(`Unable to disconnect user`, error));
5762
setClient(undefined);
5863
};
59-
}, [apiKey, callId, isAnon, token, type, user]);
64+
}, [apiKey, callId, token, type, user]);
6065

6166
return client;
6267
};

sample-apps/react/react-dogfood/helpers/client.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,32 @@ export const getClient = (
3838
storageKey: '@pronto/device-preferences',
3939
},
4040
};
41-
const tokenProvider = createTokenProvider(creds.user.id, environment);
42-
const authOptions = creds.userToken
43-
? {
44-
token: creds.userToken,
45-
tokenProvider,
46-
}
47-
: tokenProvider
48-
? { tokenProvider }
49-
: undefined;
41+
if (creds.user.type === 'guest' || creds.user.type === 'anonymous') {
42+
client = new StreamVideoClient({
43+
apiKey: creds.apiKey,
44+
user: creds.user,
45+
options,
46+
});
47+
} else {
48+
const tokenProvider = createTokenProvider(creds.user.id, environment);
49+
if (!creds.userToken && !tokenProvider) {
50+
throw new Error(
51+
'Cannot initialize StreamVideoClient with an authenticated user without token or tokenProvider',
52+
);
53+
}
5054

51-
if (!authOptions) {
52-
throw new Error(
53-
'Cannot initialize StreamVideoClient with a user without token or tokenProvider',
54-
);
55+
client = new StreamVideoClient({
56+
apiKey: creds.apiKey,
57+
user: creds.user,
58+
...(creds.userToken
59+
? {
60+
token: creds.userToken,
61+
...(tokenProvider ? { tokenProvider } : {}),
62+
}
63+
: { tokenProvider: tokenProvider! }),
64+
options,
65+
});
5566
}
56-
57-
client = new StreamVideoClient({
58-
apiKey: creds.apiKey,
59-
user: creds.user,
60-
...authOptions,
61-
options,
62-
});
6367
}
6468

6569
return client;

0 commit comments

Comments
 (0)