From 0eb81524c50dfb9c71bd8ceefa69164d9ff1518a Mon Sep 17 00:00:00 2001 From: lukasIO Date: Thu, 29 Feb 2024 21:29:21 +0100 Subject: [PATCH] Expose protobuf TrackSource and map TrackSource claims to string (#145) * Expose protobuf TrackSource and map TrackSource claims to string * Create heavy-chefs-refuse.md * Fix implementation and add unit test --- .changeset/heavy-chefs-refuse.md | 5 +++++ src/AccessToken.ts | 5 +++-- src/grants.test.ts | 27 +++++++++++++++++++++++++++ src/grants.ts | 30 +++++++++++++++++++++++++----- src/index.ts | 1 + 5 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 .changeset/heavy-chefs-refuse.md create mode 100644 src/grants.test.ts diff --git a/.changeset/heavy-chefs-refuse.md b/.changeset/heavy-chefs-refuse.md new file mode 100644 index 00000000..4ea168d8 --- /dev/null +++ b/.changeset/heavy-chefs-refuse.md @@ -0,0 +1,5 @@ +--- +"livekit-server-sdk": minor +--- + +Expose protobuf TrackSource and map TrackSource claims to string diff --git a/src/AccessToken.ts b/src/AccessToken.ts index 5ca6471b..e5c0ab6a 100644 --- a/src/AccessToken.ts +++ b/src/AccessToken.ts @@ -1,5 +1,5 @@ import * as jose from 'jose'; -import { ClaimGrants, VideoGrant } from './grants.js'; +import { ClaimGrants, VideoGrant, claimsToJwtPayload } from './grants.js'; // 6 hours const defaultTTL = `6h`; @@ -111,7 +111,8 @@ export class AccessToken { // TODO: check for video grant validity const secret = new TextEncoder().encode(this.apiSecret); - const jwt = new jose.SignJWT(this.grants) + + const jwt = new jose.SignJWT(claimsToJwtPayload(this.grants)) .setProtectedHeader({ alg: 'HS256' }) .setIssuer(this.apiKey) .setExpirationTime(this.ttl) diff --git a/src/grants.test.ts b/src/grants.test.ts new file mode 100644 index 00000000..f597084a --- /dev/null +++ b/src/grants.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'vitest'; +import { ClaimGrants, VideoGrant, claimsToJwtPayload } from './grants'; +import { TrackSource } from './proto/livekit_models_pb'; + +describe('ClaimGrants are parsed correctly', () => { + it('parses TrackSource correctly to strings', () => { + const grant: VideoGrant = { + canPublishSources: [ + TrackSource.CAMERA, + TrackSource.MICROPHONE, + TrackSource.SCREEN_SHARE, + TrackSource.SCREEN_SHARE_AUDIO, + ], + }; + + const claim: ClaimGrants = { video: grant }; + + const jwtPayload = claimsToJwtPayload(claim); + expect(jwtPayload.video).toBeTypeOf('object'); + expect(jwtPayload.video?.canPublishSources).toEqual([ + 'camera', + 'microphone', + 'screen_share', + 'screen_share_audio', + ]); + }); +}); diff --git a/src/grants.ts b/src/grants.ts index 05781b3f..bdcb5642 100644 --- a/src/grants.ts +++ b/src/grants.ts @@ -1,10 +1,30 @@ import { JWTPayload } from 'jose'; +import { TrackSource } from './proto/livekit_models_pb'; + +export function trackSourceToString(source: TrackSource) { + switch (source) { + case TrackSource.CAMERA: + return 'camera'; + case TrackSource.MICROPHONE: + return 'microphone'; + case TrackSource.SCREEN_SHARE: + return 'screen_share'; + case TrackSource.SCREEN_SHARE_AUDIO: + return 'screen_share_audio'; + default: + throw new TypeError(`Cannot convert TrackSource ${source} to string`); + } +} -export enum TrackSource { - CAMERA = 'camera', - MICROPHONE = 'microphone', - SCREEN_SHARE = 'screen_share', - SCREEN_SHARE_AUDIO = 'screen_share_audio', +export function claimsToJwtPayload( + grant: ClaimGrants, +): JWTPayload & { video?: Record } { + const claim: Record = { ...grant }; + // eslint-disable-next-line no-restricted-syntax + if (Array.isArray(claim.video?.canPublishSources)) { + claim.video.canPublishSources = claim.video.canPublishSources.map(trackSourceToString); + } + return claim; } export interface VideoGrant { diff --git a/src/index.ts b/src/index.ts index bf569ccb..f45ac818 100644 --- a/src/index.ts +++ b/src/index.ts @@ -45,4 +45,5 @@ export { Room, TrackInfo, TrackType, + TrackSource, } from './proto/livekit_models_pb.js';