// SPDX-FileCopyrightText: 2024 LiveKit, Inc. // // SPDX-License-Identifier: Apache-2.0 import { RoomAgentDispatch, RoomCompositeEgressRequest, RoomConfiguration, RoomEgress, } from '@livekit/protocol'; import * as jose from 'jose'; import { describe, expect, it } from 'vitest'; import { AccessToken, TokenVerifier } from './AccessToken.js'; import type { ClaimGrants } from './grants.js'; const testApiKey = 'abcdefg'; const testSecret = 'abababa'; describe('encoded tokens are valid', () => { const t = new AccessToken(testApiKey, testSecret, { identity: 'me', name: 'myname', }); t.addGrant({ room: 'myroom' }); const EncodedTestSecret = new TextEncoder().encode(testSecret); it('can be decoded', async () => { const { payload } = await jose.jwtVerify(await t.toJwt(), EncodedTestSecret, { issuer: testApiKey, }); expect(payload).not.toBe(undefined); }); it('has name set', async () => { const { payload } = await jose.jwtVerify(await t.toJwt(), EncodedTestSecret, { issuer: testApiKey, }); expect(payload.name).toBe('myname'); }); it('has video grants set', async () => { const { payload } = await jose.jwtVerify(await t.toJwt(), EncodedTestSecret, { issuer: testApiKey, }); expect(payload.video).toBeTruthy(); expect((payload as ClaimGrants).video?.room).toEqual('myroom'); }); }); describe('identity is required for only join grants', () => { it('allows empty identity for create', async () => { const t = new AccessToken(testApiKey, testSecret); t.addGrant({ roomCreate: true }); expect(await t.toJwt()).toBeTruthy(); }); it('throws error when identity is not provided for join', async () => { const t = new AccessToken(testApiKey, testSecret); t.addGrant({ roomJoin: true }); await expect(t.toJwt()).rejects.toThrow(); }); }); describe('verify token is valid', () => { it('can decode encoded token', async () => { const t = new AccessToken(testApiKey, testSecret); t.sha256 = 'abcdefg'; t.kind = 'agent'; t.addGrant({ roomCreate: true }); t.attributes = { foo: 'bar', live: 'kit' }; const v = new TokenVerifier(testApiKey, testSecret); const decoded = await v.verify(await t.toJwt()); expect(decoded).not.toBe(undefined); expect(decoded.sha256).toEqual('abcdefg'); expect(decoded.video?.roomCreate).toBeTruthy(); expect(decoded.kind).toEqual('agent'); expect(decoded.attributes).toEqual(t.attributes); }); }); describe('adding grants should not overwrite existing grants', () => { const EncodedTestSecret = new TextEncoder().encode(testSecret); it('should not overwrite existing grants', async () => { const t = new AccessToken(testApiKey, testSecret, { identity: 'me', name: 'myname', }); t.addGrant({ roomCreate: true }); t.addGrant({ roomJoin: true }); const { payload }: jose.JWTVerifyResult = await jose.jwtVerify( await t.toJwt(), EncodedTestSecret, { issuer: testApiKey }, ); expect(payload.video?.roomCreate).toBeTruthy(); expect(payload.video?.roomJoin).toBeTruthy(); }); }); describe('room configuration with agents and egress', () => { it('should set agents and egress in room configuration', async () => { const t = new AccessToken(testApiKey, testSecret, { identity: 'test-identity', }); const roomConfig = new RoomConfiguration({ name: 'test-room', maxParticipants: 10, }); const agents: RoomAgentDispatch[] = [ new RoomAgentDispatch({ agentName: 'agent1', metadata: 'metadata-1', }), new RoomAgentDispatch({ agentName: 'agent2', metadata: 'metadata-2', }), ]; const egress = new RoomEgress({ room: new RoomCompositeEgressRequest({ roomName: 'test-room' }), }); roomConfig.agents = agents; roomConfig.egress = egress; t.roomConfig = roomConfig; const v = new TokenVerifier(testApiKey, testSecret); const decoded = await v.verify(await t.toJwt()); expect(decoded.roomConfig).toBeDefined(); expect(decoded.roomConfig?.name).toEqual('test-room'); expect(decoded.roomConfig?.maxParticipants).toEqual(10); expect(decoded.roomConfig?.agents).toHaveLength(2); expect(decoded.roomConfig?.agents?.[0]?.agentName).toEqual('agent1'); expect(decoded.roomConfig?.agents?.[0]?.metadata).toEqual('metadata-1'); expect(decoded.roomConfig?.agents?.[1]?.agentName).toEqual('agent2'); expect(decoded.roomConfig?.agents?.[1]?.metadata).toEqual('metadata-2'); expect(decoded.roomConfig?.egress?.room?.roomName).toEqual('test-room'); }); });