docs
0
# JWT Module
1
2
The JWT module provides JWT token signing utilities for secure playback, DRM license acquisition, and signed statistics requests. JWT tokens enable access control for video content and analytics data.
3
4
## Imports
5
6
```typescript
7
import Mux from '@mux/mux-node';
8
9
const client = new Mux({
10
tokenId: 'YOUR_TOKEN_ID',
11
tokenSecret: 'YOUR_TOKEN_SECRET',
12
jwtSigningKey: 'YOUR_SIGNING_KEY_ID',
13
jwtPrivateKey: 'YOUR_PRIVATE_KEY',
14
});
15
16
// Access JWT functionality
17
const jwt = client.jwt;
18
```
19
20
## Capabilities
21
22
### Sign Playback ID (Single Token)
23
24
Create a JWT token for signed playback ID access.
25
26
```typescript { .api }
27
interface Jwt {
28
/**
29
* Create a JWT token for signed playback ID
30
* @param playbackId Playback ID to sign
31
* @param config JWT signing options
32
* @returns JWT token string
33
*/
34
signPlaybackId(
35
playbackId: string,
36
config?: MuxJWTSignOptions<TypeClaim>
37
): Promise<string>;
38
}
39
40
interface MuxJWTSignOptions<T> {
41
/** Token type (e.g., 'video', 'thumbnail', 'gif', 'storyboard', 'drm_license') */
42
type?: T;
43
/** Token expiration (e.g., '7d', '1h', '30m') - default: '7d' */
44
expiration?: string;
45
/** Additional JWT claims */
46
params?: Record<string, any>;
47
/** Signing key ID (overrides client.jwtSigningKey) */
48
keyId?: string;
49
/** Private key (overrides client.jwtPrivateKey) */
50
keySecret?: string;
51
/** Path to private key file (overrides client.jwtPrivateKey) */
52
keyFilePath?: string;
53
}
54
55
enum TypeClaim {
56
video = 'v',
57
thumbnail = 't',
58
gif = 'g',
59
storyboard = 's',
60
stats = 'playback_id',
61
drm_license = 'd',
62
}
63
```
64
65
**Usage Example:**
66
67
```typescript
68
// Sign a playback ID for video playback
69
const token = await client.jwt.signPlaybackId('PLAYBACK_ID', {
70
type: 'video',
71
expiration: '1h',
72
params: {
73
aud: 'v', // Audience claim
74
},
75
});
76
77
// Use in playback URL
78
const playbackUrl = `https://stream.mux.com/PLAYBACK_ID.m3u8?token=${token}`;
79
```
80
81
### Sign Playback ID (Multiple Tokens)
82
83
Create multiple JWT tokens for different media types in one call.
84
85
```typescript { .api }
86
interface Jwt {
87
/**
88
* Create multiple JWT tokens for different media types
89
* @param playbackId Playback ID to sign
90
* @param config JWT signing options with multiple types
91
* @returns Object containing tokens for each type
92
*/
93
signPlaybackId(
94
playbackId: string,
95
config?: MuxJWTSignOptionsMultiple<TypeClaim>
96
): Promise<Tokens>;
97
}
98
99
interface MuxJWTSignOptionsMultiple<T> {
100
/**
101
* Array of token types or [type, params] tuples
102
* @example ['video', 'thumbnail', 'storyboard']
103
* @example [['video', { time: '10' }], 'thumbnail']
104
*/
105
type?: Array<T | [T, Record<string, any>]>;
106
/** Token expiration (e.g., '7d', '1h', '30m') - default: '7d' */
107
expiration?: string;
108
/** Base JWT claims applied to all tokens */
109
params?: Record<string, any>;
110
/** Signing key ID (overrides client.jwtSigningKey) */
111
keyId?: string;
112
/** Private key (overrides client.jwtPrivateKey) */
113
keySecret?: string;
114
/** Path to private key file (overrides client.jwtPrivateKey) */
115
keyFilePath?: string;
116
}
117
118
interface Tokens {
119
'playback-token'?: string;
120
'thumbnail-token'?: string;
121
'gif-token'?: string;
122
'storyboard-token'?: string;
123
'drm-token'?: string;
124
'stats-token'?: string;
125
}
126
```
127
128
**Usage Example:**
129
130
```typescript
131
// Create tokens for video, thumbnail, and storyboard
132
const tokens = await client.jwt.signPlaybackId('PLAYBACK_ID', {
133
type: [
134
'video',
135
['thumbnail', { time: '10' }], // Thumbnail at 10 seconds
136
'storyboard',
137
],
138
expiration: '1h',
139
});
140
141
// Use different tokens for different purposes
142
const videoUrl = `https://stream.mux.com/PLAYBACK_ID.m3u8?token=${tokens.video}`;
143
const thumbnailUrl = `https://image.mux.com/PLAYBACK_ID/thumbnail.jpg?token=${tokens.thumbnail}`;
144
const storyboardUrl = `https://image.mux.com/PLAYBACK_ID/storyboard.jpg?token=${tokens.storyboard}`;
145
```
146
147
### Sign DRM License
148
149
Create a JWT token for DRM license acquisition.
150
151
```typescript { .api }
152
interface Jwt {
153
/**
154
* Create a JWT token for DRM license acquisition
155
* @param playbackId Playback ID with DRM policy
156
* @param config JWT signing options
157
* @returns JWT token for DRM license requests
158
*/
159
signDrmLicense(
160
playbackId: string,
161
config?: MuxJWTSignOptions<TypeClaim>
162
): Promise<string>;
163
}
164
```
165
166
**Usage Example:**
167
168
```typescript
169
// Create DRM license token
170
const drmToken = await client.jwt.signDrmLicense('PLAYBACK_ID', {
171
expiration: '24h',
172
});
173
174
// Use with DRM-protected content
175
// The player will automatically request a license using this token
176
```
177
178
### Sign Viewer Counts
179
180
Create a JWT token for signed statistics requests.
181
182
```typescript { .api }
183
interface Jwt {
184
/**
185
* Create a JWT token for signed statistics request
186
* @param id Asset or live stream ID
187
* @param config JWT signing options
188
* @returns JWT token for viewer count requests
189
*/
190
signViewerCounts(
191
id: string,
192
config?: MuxJWTSignOptions<DataTypeClaim>
193
): Promise<string>;
194
}
195
196
enum DataTypeClaim {
197
video = 'video_id',
198
asset = 'asset_id',
199
playback = 'playback_id',
200
live_stream = 'live_stream_id',
201
}
202
```
203
204
**Usage Example:**
205
206
```typescript
207
// Create token for video viewer counts
208
const viewerToken = await client.jwt.signViewerCounts('ASSET_ID', {
209
type: 'video',
210
expiration: '7d',
211
});
212
213
// Use to access viewer statistics
214
```
215
216
### Sign Space ID (Deprecated)
217
218
Create a JWT token for Mux Real-Time Video spaces (deprecated).
219
220
```typescript { .api }
221
interface Jwt {
222
/**
223
* Create a JWT token for Mux Real-Time Video spaces
224
* @deprecated Mux Real-Time Video has been shut down
225
* @param spaceId Space ID to sign
226
* @param config JWT signing options
227
* @returns JWT token
228
*/
229
signSpaceId(
230
spaceId: string,
231
config?: MuxJWTSignOptions<never>
232
): Promise<string>;
233
}
234
```
235
236
**Note:** This method is deprecated as Mux Real-Time Video has been shut down. It will be removed in the next major version.
237
238
## Token Types and Audience Claims
239
240
Different token types use different audience claims:
241
242
```typescript { .api }
243
/** Audience claims for playback tokens */
244
enum TypeClaim {
245
/** Video playback */
246
video = 'v',
247
/** Thumbnail images */
248
thumbnail = 't',
249
/** Animated GIFs */
250
gif = 'g',
251
/** Storyboard images */
252
storyboard = 's',
253
/** Statistics (not supported by Mux Player) */
254
stats = 'playback_id',
255
/** DRM license */
256
drm_license = 'd',
257
}
258
259
/** Audience claims for data/statistics tokens */
260
enum DataTypeClaim {
261
/** Video data */
262
video = 'video_id',
263
/** Asset data */
264
asset = 'asset_id',
265
/** Playback data */
266
playback = 'playback_id',
267
/** Live stream data */
268
live_stream = 'live_stream_id',
269
}
270
```
271
272
## Common Patterns
273
274
### Creating Signed Playback IDs
275
276
```typescript
277
// Step 1: Create an asset with signed playback policy
278
const asset = await client.video.assets.create({
279
inputs: [{ url: 'https://example.com/video.mp4' }],
280
playback_policies: ['signed'],
281
});
282
283
// Step 2: Create a playback ID (if needed)
284
const playbackId = await client.video.assets.createPlaybackId(asset.id, {
285
policy: 'signed',
286
});
287
288
// Step 3: Sign the playback ID
289
const token = await client.jwt.signPlaybackId(playbackId.id, {
290
type: 'video',
291
expiration: '1h',
292
});
293
294
// Step 4: Use in playback URL
295
const url = `https://stream.mux.com/${playbackId.id}.m3u8?token=${token}`;
296
```
297
298
### Custom JWT Claims
299
300
Add custom claims to JWT tokens:
301
302
```typescript
303
const token = await client.jwt.signPlaybackId('PLAYBACK_ID', {
304
type: 'video',
305
expiration: '2h',
306
params: {
307
aud: 'v', // Audience
308
sub: 'user-123', // Subject (user ID)
309
custom_claim: 'custom_value', // Custom claim
310
},
311
});
312
```
313
314
### Time-Based Access
315
316
Control when content can be accessed:
317
318
```typescript
319
const token = await client.jwt.signPlaybackId('PLAYBACK_ID', {
320
type: 'video',
321
params: {
322
// Content not valid before this time
323
nbf: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
324
325
// Content expires at this time
326
exp: Math.floor(Date.now() / 1000) + 7200, // 2 hours from now
327
},
328
});
329
```
330
331
### Restricting Playback Domain
332
333
Limit playback to specific domains:
334
335
```typescript
336
const token = await client.jwt.signPlaybackId('PLAYBACK_ID', {
337
type: 'video',
338
expiration: '1h',
339
params: {
340
// Only allow playback from these domains
341
aud: 'v',
342
// Custom domain restriction (requires server-side validation)
343
allowed_domains: ['example.com', 'www.example.com'],
344
},
345
});
346
```
347
348
### Using Different Signing Keys
349
350
Override the default signing key for specific tokens:
351
352
```typescript
353
const token = await client.jwt.signPlaybackId('PLAYBACK_ID', {
354
type: 'video',
355
expiration: '1h',
356
keyId: 'DIFFERENT_KEY_ID',
357
keySecret: 'DIFFERENT_PRIVATE_KEY',
358
});
359
```
360
361
## Expiration Format
362
363
The `expiration` parameter accepts various time formats:
364
365
- `'7d'` - 7 days
366
- `'24h'` - 24 hours
367
- `'30m'` - 30 minutes
368
- `'1h30m'` - 1 hour 30 minutes
369
- `'1w'` - 1 week
370
371
Default expiration is `'7d'` (7 days) if not specified.
372
373
## Security Best Practices
374
375
1. **Keep private keys secure**: Never expose private keys in client-side code or version control.
376
377
2. **Use short expirations**: For sensitive content, use shorter token expiration times (e.g., 1-2 hours).
378
379
3. **Rotate signing keys**: Periodically rotate signing keys for enhanced security.
380
381
4. **Use HTTPS**: Always serve signed content over HTTPS to prevent token interception.
382
383
5. **Combine with other restrictions**: Use JWT tokens alongside playback restrictions for defense in depth.
384
385
6. **Don't embed tokens in URLs for public content**: URLs can be logged and shared. For sensitive content, deliver tokens dynamically.
386
387
7. **Validate custom claims server-side**: Custom claims should be validated by your application logic.
388
389
## Token Workflow
390
391
Complete workflow for signed playback:
392
393
```typescript
394
// 1. Create asset with signed policy
395
const asset = await client.video.assets.create({
396
inputs: [{ url: 'https://example.com/video.mp4' }],
397
playback_policies: ['signed'],
398
});
399
400
// 2. Wait for asset to be ready
401
let readyAsset;
402
do {
403
await new Promise((resolve) => setTimeout(resolve, 5000));
404
readyAsset = await client.video.assets.retrieve(asset.id);
405
} while (readyAsset.status !== 'ready');
406
407
// 3. Get playback ID
408
const playbackId = readyAsset.playback_ids[0].id;
409
410
// 4. Generate token (on-demand, per user/session)
411
const token = await client.jwt.signPlaybackId(playbackId, {
412
type: 'video',
413
expiration: '1h',
414
params: {
415
sub: 'user-123', // User identifier
416
},
417
});
418
419
// 5. Return playback URL to client
420
return {
421
url: `https://stream.mux.com/${playbackId}.m3u8?token=${token}`,
422
expiresIn: 3600, // 1 hour in seconds
423
};
424
```
425
426
## DRM Workflow
427
428
Complete workflow for DRM-protected content:
429
430
```typescript
431
// 1. Create asset with DRM policy
432
const asset = await client.video.assets.create({
433
inputs: [{ url: 'https://example.com/video.mp4' }],
434
playback_policies: ['drm'],
435
});
436
437
// 2. Create DRM playback ID
438
const playbackId = await client.video.assets.createPlaybackId(asset.id, {
439
policy: 'drm',
440
drm_configuration_id: 'DRM_CONFIG_ID',
441
});
442
443
// 3. Generate DRM license token
444
const drmToken = await client.jwt.signDrmLicense(playbackId.id, {
445
expiration: '24h',
446
});
447
448
// 4. Return playback info to client
449
return {
450
playbackId: playbackId.id,
451
drmToken: drmToken,
452
// Client player will use drmToken to acquire DRM license
453
};
454
```
455