or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analytics-metrics.mdclient-setup.mddata.mddelivery-usage.mderror-handling.mdindex.mdjwt-signing.mdjwt.mdlive-streaming.mdplayback-control.mdsystem-operations.mdsystem.mdtranscription-vocabularies.mdupload-utilities.mdvideo-assets.mdvideo-playback.mdvideo-uploads.mdvideo.mdweb-inputs.mdwebhooks.md

jwt.mddocs/

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