livekit-server-sdk 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -95
- package/dist/AccessToken.d.ts +1 -0
- package/dist/AccessToken.d.ts.map +1 -0
- package/dist/AccessToken.js +3 -0
- package/dist/AccessToken.js.map +1 -1
- package/dist/EgressClient.d.ts +1 -0
- package/dist/EgressClient.d.ts.map +1 -0
- package/dist/EgressClient.js +4 -1
- package/dist/EgressClient.js.map +1 -1
- package/dist/IngressClient.d.ts +1 -0
- package/dist/IngressClient.d.ts.map +1 -0
- package/dist/IngressClient.js +3 -0
- package/dist/IngressClient.js.map +1 -1
- package/dist/RoomServiceClient.d.ts +1 -0
- package/dist/RoomServiceClient.d.ts.map +1 -0
- package/dist/RoomServiceClient.js.map +1 -1
- package/dist/ServiceBase.d.ts +1 -0
- package/dist/ServiceBase.d.ts.map +1 -0
- package/dist/ServiceBase.js +3 -0
- package/dist/ServiceBase.js.map +1 -1
- package/dist/SipClient.d.ts +7 -1
- package/dist/SipClient.d.ts.map +1 -0
- package/dist/SipClient.js +22 -4
- package/dist/SipClient.js.map +1 -1
- package/dist/TwirpRPC.d.ts +1 -0
- package/dist/TwirpRPC.d.ts.map +1 -0
- package/dist/TwirpRPC.js.map +1 -1
- package/dist/WebhookReceiver.d.ts +2 -1
- package/dist/WebhookReceiver.d.ts.map +1 -0
- package/dist/WebhookReceiver.js.map +1 -1
- package/dist/grants.d.ts +2 -1
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +3 -0
- package/dist/grants.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/src/AccessToken.test.ts +96 -0
- package/src/AccessToken.ts +151 -0
- package/src/EgressClient.ts +617 -0
- package/src/IngressClient.ts +257 -0
- package/src/RoomServiceClient.ts +358 -0
- package/src/ServiceBase.ts +35 -0
- package/src/SipClient.ts +277 -0
- package/src/TwirpRPC.ts +54 -0
- package/src/WebhookReceiver.test.ts +44 -0
- package/src/WebhookReceiver.ts +85 -0
- package/src/grants.test.ts +30 -0
- package/src/grants.ts +96 -0
- package/src/index.ts +53 -0
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.eslintrc.cjs +0 -17
- package/.github/banner_dark.png +0 -0
- package/.github/banner_light.png +0 -0
- package/.github/workflows/release.yaml +0 -46
- package/.github/workflows/test.yaml +0 -46
- package/.gitmodules +0 -3
- package/.prettierignore +0 -8
- package/CHANGELOG.md +0 -87
- package/LICENSE +0 -201
- package/NOTICE +0 -13
- package/renovate.json +0 -18
- package/tsconfig.eslint.json +0 -5
- package/vite.config.js +0 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebhookReceiver.js","sourceRoot":"","sources":["../src/WebhookReceiver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WebhookReceiver.js","sourceRoot":"","sources":["../src/WebhookReceiver.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,MAAM,OAAO,YAAa,SAAQ,iBAAiB;IAAnD;;QACE,UAAK,GAAsB,EAAE,CAAC;IAahC,CAAC;IAXC,MAAM,CAAC,UAAU,CAAC,KAAiB,EAAE,OAAoC;QACvE,OAAO,IAAI,YAAY,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,SAAoB,EAAE,OAAkC;QACtE,OAAO,IAAI,YAAY,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,UAAkB,EAAE,OAAkC;QAC1E,OAAO,IAAI,YAAY,EAAE,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC;CACF;AAoBD,MAAM,OAAO,eAAe;IAG1B,YAAY,MAAc,EAAE,SAAiB;QAC3C,IAAI,CAAC,QAAQ,GAAG,IAAI,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CACX,IAAY,EACZ,UAAmB,EACnB,WAAoB,KAAK;QAEzB,eAAe;QACf,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACtD,cAAc;YACd,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACzE,MAAM,WAAW,GAAG,IAAI,CACtB,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;iBAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;iBAClC,IAAI,CAAC,EAAE,CAAC,CACZ,CAAC;YAEF,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;CACF"}
|
package/dist/grants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { JWTPayload } from 'jose';
|
|
2
1
|
import { TrackSource } from '@livekit/protocol';
|
|
2
|
+
import { JWTPayload } from 'jose';
|
|
3
3
|
export declare function trackSourceToString(source: TrackSource): "camera" | "microphone" | "screen_share" | "screen_share_audio";
|
|
4
4
|
export declare function claimsToJwtPayload(grant: ClaimGrants): JWTPayload & {
|
|
5
5
|
video?: Record<string, unknown>;
|
|
@@ -53,3 +53,4 @@ export interface ClaimGrants extends JWTPayload {
|
|
|
53
53
|
metadata?: string;
|
|
54
54
|
sha256?: string;
|
|
55
55
|
}
|
|
56
|
+
//# sourceMappingURL=grants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grants.d.ts","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,mEAatD;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,GACjB,UAAU,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAOlD;AAED,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,sCAAsC;IACtC,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,0EAA0E;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,WAAW,EAAE,CAAC;IAElC,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;OAEG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,gBAAgB;AAChB,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
package/dist/grants.js
CHANGED
package/dist/grants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"grants.js","sourceRoot":"","sources":["../src/grants.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,UAAU,mBAAmB,CAAC,MAAmB;IACrD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,WAAW,CAAC,MAAM;YACrB,OAAO,QAAQ,CAAC;QAClB,KAAK,WAAW,CAAC,UAAU;YACzB,OAAO,YAAY,CAAC;QACtB,KAAK,WAAW,CAAC,YAAY;YAC3B,OAAO,cAAc,CAAC;QACxB,KAAK,WAAW,CAAC,kBAAkB;YACjC,OAAO,oBAAoB,CAAC;QAC9B;YACE,MAAM,IAAI,SAAS,CAAC,8BAA8B,MAAM,YAAY,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAAkB;;IAElB,MAAM,KAAK,qBAA6B,KAAK,CAAE,CAAC;IAChD,gDAAgD;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAA,KAAK,CAAC,KAAK,0CAAE,iBAAiB,CAAC,EAAE,CAAC;QAClD,KAAK,CAAC,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,3 +6,4 @@ export * from './RoomServiceClient.js';
|
|
|
6
6
|
export * from './WebhookReceiver.js';
|
|
7
7
|
export * from './grants.js';
|
|
8
8
|
export { AliOSSUpload, AzureBlobUpload, DirectFileOutput, EgressInfo, EncodedFileOutput, EncodedFileType, EncodingOptions, EncodingOptionsPreset, GCPUpload, ImageOutput, ParticipantEgressRequest, RoomCompositeEgressRequest, S3Upload, SegmentedFileOutput, SegmentedFileProtocol, StreamOutput, StreamProtocol, TrackCompositeEgressRequest, TrackEgressRequest, WebEgressRequest, IngressAudioEncodingOptions, IngressAudioEncodingPreset, IngressAudioOptions, IngressInfo, IngressInput, IngressState, IngressVideoEncodingOptions, IngressVideoEncodingPreset, IngressVideoOptions, DataPacket_Kind, ParticipantInfo, ParticipantInfo_State, ParticipantPermission, Room, TrackInfo, TrackType, TrackSource, SIPTrunkInfo, SIPDispatchRuleInfo, SIPParticipantInfo, } from '@livekit/protocol';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,wBAAwB,EACxB,0BAA0B,EAC1B,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,2BAA2B,EAC3B,kBAAkB,EAClB,gBAAgB,EAChB,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,IAAI,EACJ,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,wBAAwB,EACxB,0BAA0B,EAC1B,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,2BAA2B,EAC3B,kBAAkB,EAClB,gBAAgB,EAChB,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,IAAI,EACJ,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,sCAAsC;AAEtC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,aAAa,CAAC;AAC5B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,wBAAwB,EACxB,0BAA0B,EAC1B,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,2BAA2B,EAC3B,kBAAkB,EAClB,gBAAgB,EAChB,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,2BAA2B,EAC3B,0BAA0B,EAC1B,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,IAAI,EACJ,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livekit-server-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Server-side SDK for LiveKit",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
"author": "David Zhao <david@davidzhao.com>",
|
|
9
9
|
"license": "Apache-2.0",
|
|
10
10
|
"type": "module",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
11
15
|
"dependencies": {
|
|
12
16
|
"@livekit/protocol": "^1.14.0",
|
|
13
17
|
"camelcase-keys": "^9.0.0",
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import * as jose from 'jose';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
import { AccessToken, TokenVerifier } from './AccessToken';
|
|
7
|
+
import { ClaimGrants } from './grants';
|
|
8
|
+
|
|
9
|
+
const testApiKey = 'abcdefg';
|
|
10
|
+
const testSecret = 'abababa';
|
|
11
|
+
|
|
12
|
+
describe('encoded tokens are valid', () => {
|
|
13
|
+
const t = new AccessToken(testApiKey, testSecret, {
|
|
14
|
+
identity: 'me',
|
|
15
|
+
name: 'myname',
|
|
16
|
+
});
|
|
17
|
+
t.addGrant({ room: 'myroom' });
|
|
18
|
+
const EncodedTestSecret = new TextEncoder().encode(testSecret);
|
|
19
|
+
it('can be decoded', async () => {
|
|
20
|
+
const { payload } = await jose.jwtVerify(await t.toJwt(), EncodedTestSecret, {
|
|
21
|
+
issuer: testApiKey,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(payload).not.toBe(undefined);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('has name set', async () => {
|
|
28
|
+
const { payload } = await jose.jwtVerify(await t.toJwt(), EncodedTestSecret, {
|
|
29
|
+
issuer: testApiKey,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(payload.name).toBe('myname');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('has video grants set', async () => {
|
|
36
|
+
const { payload } = await jose.jwtVerify(await t.toJwt(), EncodedTestSecret, {
|
|
37
|
+
issuer: testApiKey,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(payload.video).toBeTruthy();
|
|
41
|
+
expect((payload as ClaimGrants).video?.room).toEqual('myroom');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('identity is required for only join grants', () => {
|
|
46
|
+
it('allows empty identity for create', async () => {
|
|
47
|
+
const t = new AccessToken(testApiKey, testSecret);
|
|
48
|
+
t.addGrant({ roomCreate: true });
|
|
49
|
+
|
|
50
|
+
expect(await t.toJwt()).toBeTruthy();
|
|
51
|
+
});
|
|
52
|
+
it('throws error when identity is not provided for join', async () => {
|
|
53
|
+
const t = new AccessToken(testApiKey, testSecret);
|
|
54
|
+
t.addGrant({ roomJoin: true });
|
|
55
|
+
|
|
56
|
+
await expect(async () => {
|
|
57
|
+
await t.toJwt();
|
|
58
|
+
}).rejects.toThrow();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('verify token is valid', () => {
|
|
63
|
+
it('can decode encoded token', async () => {
|
|
64
|
+
const t = new AccessToken(testApiKey, testSecret);
|
|
65
|
+
t.sha256 = 'abcdefg';
|
|
66
|
+
t.addGrant({ roomCreate: true });
|
|
67
|
+
|
|
68
|
+
const v = new TokenVerifier(testApiKey, testSecret);
|
|
69
|
+
const decoded = await v.verify(await t.toJwt());
|
|
70
|
+
|
|
71
|
+
expect(decoded).not.toBe(undefined);
|
|
72
|
+
expect(decoded.sha256).toEqual('abcdefg');
|
|
73
|
+
expect(decoded.video?.roomCreate).toBeTruthy();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('adding grants should not overwrite existing grants', () => {
|
|
78
|
+
const EncodedTestSecret = new TextEncoder().encode(testSecret);
|
|
79
|
+
|
|
80
|
+
it('should not overwrite existing grants', async () => {
|
|
81
|
+
const t = new AccessToken(testApiKey, testSecret, {
|
|
82
|
+
identity: 'me',
|
|
83
|
+
name: 'myname',
|
|
84
|
+
});
|
|
85
|
+
t.addGrant({ roomCreate: true });
|
|
86
|
+
t.addGrant({ roomJoin: true });
|
|
87
|
+
|
|
88
|
+
const { payload }: jose.JWTVerifyResult<ClaimGrants> = await jose.jwtVerify(
|
|
89
|
+
await t.toJwt(),
|
|
90
|
+
EncodedTestSecret,
|
|
91
|
+
{ issuer: testApiKey },
|
|
92
|
+
);
|
|
93
|
+
expect(payload.video?.roomCreate).toBeTruthy();
|
|
94
|
+
expect(payload.video?.roomJoin).toBeTruthy();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024 LiveKit, Inc.
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import * as jose from 'jose';
|
|
5
|
+
import { ClaimGrants, VideoGrant, claimsToJwtPayload } from './grants.js';
|
|
6
|
+
|
|
7
|
+
// 6 hours
|
|
8
|
+
const defaultTTL = `6h`;
|
|
9
|
+
|
|
10
|
+
export interface AccessTokenOptions {
|
|
11
|
+
/**
|
|
12
|
+
* amount of time before expiration
|
|
13
|
+
* expressed in seconds or a string describing a time span zeit/ms.
|
|
14
|
+
* eg: '2 days', '10h', or seconds as numeric value
|
|
15
|
+
*/
|
|
16
|
+
ttl?: number | string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* display name for the participant, available as `Participant.name`
|
|
20
|
+
*/
|
|
21
|
+
name?: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* identity of the user, required for room join tokens
|
|
25
|
+
*/
|
|
26
|
+
identity?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* custom metadata to be passed to participants
|
|
30
|
+
*/
|
|
31
|
+
metadata?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class AccessToken {
|
|
35
|
+
private apiKey: string;
|
|
36
|
+
|
|
37
|
+
private apiSecret: string;
|
|
38
|
+
|
|
39
|
+
private grants: ClaimGrants;
|
|
40
|
+
|
|
41
|
+
identity?: string;
|
|
42
|
+
|
|
43
|
+
ttl: number | string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new AccessToken
|
|
47
|
+
* @param apiKey API Key, can be set in env LIVEKIT_API_KEY
|
|
48
|
+
* @param apiSecret Secret, can be set in env LIVEKIT_API_SECRET
|
|
49
|
+
*/
|
|
50
|
+
constructor(apiKey?: string, apiSecret?: string, options?: AccessTokenOptions) {
|
|
51
|
+
if (!apiKey) {
|
|
52
|
+
apiKey = process.env.LIVEKIT_API_KEY;
|
|
53
|
+
}
|
|
54
|
+
if (!apiSecret) {
|
|
55
|
+
apiSecret = process.env.LIVEKIT_API_SECRET;
|
|
56
|
+
}
|
|
57
|
+
if (!apiKey || !apiSecret) {
|
|
58
|
+
throw Error('api-key and api-secret must be set');
|
|
59
|
+
} else if (typeof document !== 'undefined') {
|
|
60
|
+
// check against document rather than window because deno provides window
|
|
61
|
+
console.error(
|
|
62
|
+
'You should not include your API secret in your web client bundle.\n\n' +
|
|
63
|
+
'Your web client should request a token from your backend server which should then use ' +
|
|
64
|
+
'the API secret to generate a token. See https://docs.livekit.io/client/connect/',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
this.apiKey = apiKey;
|
|
68
|
+
this.apiSecret = apiSecret;
|
|
69
|
+
this.grants = {};
|
|
70
|
+
this.identity = options?.identity;
|
|
71
|
+
this.ttl = options?.ttl || defaultTTL;
|
|
72
|
+
if (typeof this.ttl === 'number') {
|
|
73
|
+
this.ttl = `${this.ttl}s`;
|
|
74
|
+
}
|
|
75
|
+
if (options?.metadata) {
|
|
76
|
+
this.metadata = options.metadata;
|
|
77
|
+
}
|
|
78
|
+
if (options?.name) {
|
|
79
|
+
this.name = options.name;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Adds a video grant to this token.
|
|
85
|
+
* @param grant
|
|
86
|
+
*/
|
|
87
|
+
addGrant(grant: VideoGrant) {
|
|
88
|
+
this.grants.video = { ...(this.grants.video ?? {}), ...grant };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Set metadata to be passed to the Participant, used only when joining the room
|
|
93
|
+
*/
|
|
94
|
+
set metadata(md: string) {
|
|
95
|
+
this.grants.metadata = md;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
set name(name: string) {
|
|
99
|
+
this.grants.name = name;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get sha256(): string | undefined {
|
|
103
|
+
return this.grants.sha256;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
set sha256(sha: string | undefined) {
|
|
107
|
+
this.grants.sha256 = sha;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @returns JWT encoded token
|
|
112
|
+
*/
|
|
113
|
+
async toJwt(): Promise<string> {
|
|
114
|
+
// TODO: check for video grant validity
|
|
115
|
+
|
|
116
|
+
const secret = new TextEncoder().encode(this.apiSecret);
|
|
117
|
+
|
|
118
|
+
const jwt = new jose.SignJWT(claimsToJwtPayload(this.grants))
|
|
119
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
120
|
+
.setIssuer(this.apiKey)
|
|
121
|
+
.setExpirationTime(this.ttl)
|
|
122
|
+
.setNotBefore(0);
|
|
123
|
+
if (this.identity) {
|
|
124
|
+
jwt.setSubject(this.identity);
|
|
125
|
+
} else if (this.grants.video?.roomJoin) {
|
|
126
|
+
throw Error('identity is required for join but not set');
|
|
127
|
+
}
|
|
128
|
+
return jwt.sign(secret);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export class TokenVerifier {
|
|
133
|
+
private apiKey: string;
|
|
134
|
+
|
|
135
|
+
private apiSecret: string;
|
|
136
|
+
|
|
137
|
+
constructor(apiKey: string, apiSecret: string) {
|
|
138
|
+
this.apiKey = apiKey;
|
|
139
|
+
this.apiSecret = apiSecret;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async verify(token: string): Promise<ClaimGrants> {
|
|
143
|
+
const secret = new TextEncoder().encode(this.apiSecret);
|
|
144
|
+
const { payload } = await jose.jwtVerify(token, secret, { issuer: this.apiKey });
|
|
145
|
+
if (!payload) {
|
|
146
|
+
throw Error('invalid token');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return payload as ClaimGrants;
|
|
150
|
+
}
|
|
151
|
+
}
|