Skip to content

Commit befe685

Browse files
authored
feat: add option to provide role for call token (#35)
1 parent 6c5bddb commit befe685

File tree

4 files changed

+57
-42
lines changed

4 files changed

+57
-42
lines changed

__tests__/create-token.test.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,28 @@ describe('creating tokens', () => {
3737
});
3838

3939
it('with call IDs provided', () => {
40+
const call_cids = ['default:call1', 'livestream:call2'];
41+
const token = client.createCallToken(userId, call_cids);
42+
const decodedToken = jwt.verify(token, secret) as any;
43+
44+
expect(decodedToken.user_id).toEqual(userId);
45+
expect(decodedToken.call_cids).toEqual(call_cids);
46+
expect(decodedToken.iat).toBeDefined();
47+
expect(decodedToken.exp).toBeDefined();
48+
});
49+
50+
it('with call IDs and role provided', () => {
4051
const call_cids = ['default:call1', 'livestream:call2'];
4152
const token = client.createCallToken(
42-
userId,
53+
{ user_id: userId, role: 'admin' },
4354
call_cids,
44-
undefined,
45-
undefined,
4655
);
4756
const decodedToken = jwt.verify(token, secret) as any;
4857

4958
expect(decodedToken.call_cids).toEqual(call_cids);
59+
expect(decodedToken.role).toEqual('admin');
60+
expect(decodedToken.user_id).toEqual(userId);
61+
expect(decodedToken.iat).toBeDefined();
62+
expect(decodedToken.exp).toBeDefined();
5063
});
5164
});

src/StreamClient.ts

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
import { v4 as uuidv4 } from 'uuid';
5959
import { JWTServerToken, JWTUserToken } from './utils/create-token';
6060
import crypto from 'crypto';
61+
import { CallTokenPayload, UserTokenPayload } from './types';
6162

6263
export interface StreamClientOptions {
6364
timeout?: number;
@@ -141,24 +142,20 @@ export class StreamClient {
141142
iat = Math.round(Date.now() / 1000),
142143
call_cids?: string[],
143144
) {
144-
const extra: { exp?: number; iat?: number; call_cids?: string[] } = {};
145-
146-
if (exp) {
147-
extra.exp = exp;
148-
}
149-
150-
if (iat) {
151-
extra.iat = iat;
152-
}
145+
const payload: UserTokenPayload = {
146+
user_id: userID,
147+
exp,
148+
iat,
149+
};
153150

154151
if (call_cids) {
155152
console.warn(
156153
`Use createCallToken method for creating call tokens, the "call_cids" param will be removed from the createToken method with version 0.2.0`,
157154
);
158-
extra.call_cids = call_cids;
155+
payload.call_cids = call_cids;
159156
}
160157

161-
return JWTUserToken(this.secret, userID, extra);
158+
return JWTUserToken(this.secret, payload);
162159
}
163160

164161
/**
@@ -170,24 +167,26 @@ export class StreamClient {
170167
* @returns
171168
*/
172169
createCallToken(
173-
userID: string,
170+
userIdOrObject: string | { user_id: string; role?: string },
174171
call_cids: string[],
175172
exp = Math.round(new Date().getTime() / 1000) + 60 * 60,
176173
iat = Math.round(Date.now() / 1000),
177174
) {
178-
const extra: { exp?: number; iat?: number; call_cids?: string[] } = {};
179-
180-
if (exp) {
181-
extra.exp = exp;
182-
}
183-
184-
if (iat) {
185-
extra.iat = iat;
175+
const payload: CallTokenPayload = {
176+
exp,
177+
iat,
178+
call_cids,
179+
user_id:
180+
typeof userIdOrObject === 'string'
181+
? userIdOrObject
182+
: userIdOrObject.user_id,
183+
};
184+
185+
if (typeof userIdOrObject === 'object' && userIdOrObject.role) {
186+
payload.role = userIdOrObject.role;
186187
}
187188

188-
extra.call_cids = call_cids;
189-
190-
return JWTUserToken(this.secret, userID, extra);
189+
return JWTUserToken(this.secret, payload);
191190
}
192191

193192
createDevice = (createDeviceRequest: CreateDeviceRequest) => {

src/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,15 @@
11
export type OmitTypeId<T> = Omit<T, 'type' | 'id' | 'connection_id'>;
2+
3+
interface BaseTokenPayload {
4+
user_id: string;
5+
exp: number;
6+
iat: number;
7+
call_cids?: string[];
8+
}
9+
10+
export type UserTokenPayload = BaseTokenPayload;
11+
12+
export type CallTokenPayload = BaseTokenPayload & {
13+
call_cids: string[];
14+
role?: string;
15+
};

src/utils/create-token.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,19 @@ import jwt, { Secret, SignOptions } from 'jsonwebtoken';
22

33
export function JWTUserToken(
44
apiSecret: Secret,
5-
userId: string,
6-
extraData = {},
7-
jwtOptions: SignOptions = {},
5+
payload: { user_id: string; exp: number; iat: number; call_cids?: string[] },
86
) {
9-
if (typeof userId !== 'string') {
10-
throw new TypeError('userId should be a string');
11-
}
12-
13-
const payload: { user_id: string } & any = {
14-
user_id: userId,
15-
...extraData,
16-
};
17-
187
// make sure we return a clear error when jwt is shimmed (ie. browser build)
198
if (jwt == null || jwt.sign == null) {
209
throw Error(
2110
`Unable to find jwt crypto, if you are getting this error is probably because you are trying to generate tokens on browser or React Native (or other environment where crypto functions are not available). Please Note: token should only be generated server-side.`,
2211
);
2312
}
2413

25-
const opts: SignOptions = Object.assign(
26-
{ algorithm: 'HS256', noTimestamp: true },
27-
jwtOptions,
28-
);
14+
const opts: SignOptions = Object.assign({
15+
algorithm: 'HS256',
16+
noTimestamp: true,
17+
});
2918

3019
if (payload.iat) {
3120
opts.noTimestamp = false;

0 commit comments

Comments
 (0)