Merge pull request #339 from thecodingmachine/jitsijwt

Adding JWT authentication to Jitsi
This commit is contained in:
David Négrier 2020-10-19 13:45:26 +02:00 committed by GitHub
commit 84566f08f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 201 additions and 48 deletions

View file

@ -1,3 +1,7 @@
DEBUG_MODE=false DEBUG_MODE=false
JITSI_URL=meet.jit.si JITSI_URL=meet.jit.si
# If your Jitsi environment has authentication set up, you MUST set JITSI_PRIVATE_MODE to "true" and you MUST pass a SECRET_JITSI_KEY to generate the JWT secret
JITSI_PRIVATE_MODE=false
JITSI_ISS=
SECRET_JITSI_KEY=
ADMIN_API_TOKEN=123 ADMIN_API_TOKEN=123

View file

@ -121,6 +121,9 @@ jobs:
env: env:
KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }} KUBE_CONFIG_FILE: ${{ secrets.KUBE_CONFIG_FILE }}
ADMIN_API_TOKEN: ${{ secrets.ADMIN_API_TOKEN }} ADMIN_API_TOKEN: ${{ secrets.ADMIN_API_TOKEN }}
JITSI_ISS: ${{ secrets.JITSI_ISS }}
JITSI_URL: ${{ secrets.JITSI_URL }}
SECRET_JITSI_KEY: ${{ secrets.SECRET_JITSI_KEY }}
with: with:
namespace: workadventure-${{ env.GITHUB_REF_SLUG }} namespace: workadventure-${{ env.GITHUB_REF_SLUG }}

View file

@ -12,6 +12,8 @@ import {
WebRtcSignalToServerMessage, WebRtcSignalToServerMessage,
PlayGlobalMessage, PlayGlobalMessage,
ReportPlayerMessage, ReportPlayerMessage,
QueryJitsiJwtMessage,
SendJitsiJwtMessage,
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb";
import {TemplatedApp} from "uWebSockets.js" import {TemplatedApp} from "uWebSockets.js"
@ -20,6 +22,7 @@ import {jwtTokenManager} from "../Services/JWTTokenManager";
import {adminApi} from "../Services/AdminApi"; import {adminApi} from "../Services/AdminApi";
import {socketManager} from "../Services/SocketManager"; import {socketManager} from "../Services/SocketManager";
import {emitInBatch, resetPing} from "../Services/IoSocketHelpers"; import {emitInBatch, resetPing} from "../Services/IoSocketHelpers";
import Jwt from "jsonwebtoken";
export class IoSocketController { export class IoSocketController {
private nextUserId: number = 1; private nextUserId: number = 1;
@ -191,6 +194,8 @@ export class IoSocketController {
socketManager.emitPlayGlobalMessage(client, message.getPlayglobalmessage() as PlayGlobalMessage); socketManager.emitPlayGlobalMessage(client, message.getPlayglobalmessage() as PlayGlobalMessage);
} else if (message.hasReportplayermessage()){ } else if (message.hasReportplayermessage()){
socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage); socketManager.handleReportMessage(client, message.getReportplayermessage() as ReportPlayerMessage);
} else if (message.hasQueryjitsijwtmessage()){
socketManager.handleQueryJitsiJwtMessage(client, message.getQueryjitsijwtmessage() as QueryJitsiJwtMessage);
} }
/* Ok is false if backpressure was built up, wait for drain */ /* Ok is false if backpressure was built up, wait for drain */

View file

@ -6,6 +6,9 @@ const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLER
const ADMIN_API_URL = process.env.ADMIN_API_URL || 'http://admin'; const ADMIN_API_URL = process.env.ADMIN_API_URL || 'http://admin';
const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || 'myapitoken'; const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || 'myapitoken';
const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80; const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80;
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
const JITSI_ISS = process.env.JITSI_ISS || '';
const SECRET_JITSI_KEY = process.env.SECRET_JITSI_KEY || '';
export { export {
SECRET_KEY, SECRET_KEY,
@ -16,4 +19,7 @@ export {
GROUP_RADIUS, GROUP_RADIUS,
ALLOW_ARTILLERY, ALLOW_ARTILLERY,
CPU_OVERHEAT_THRESHOLD, CPU_OVERHEAT_THRESHOLD,
JITSI_URL,
JITSI_ISS,
SECRET_JITSI_KEY
} }

View file

@ -4,8 +4,8 @@ import {
GroupDeleteMessage, GroupDeleteMessage,
GroupUpdateMessage, GroupUpdateMessage,
ItemEventMessage, ItemEventMessage,
ItemStateMessage, ItemStateMessage,
PlayGlobalMessage, PlayGlobalMessage,
PointMessage, PointMessage,
PositionMessage, PositionMessage,
RoomJoinedMessage, RoomJoinedMessage,
@ -19,7 +19,7 @@ import {
UserMovesMessage, UserMovesMessage,
ViewportMessage, WebRtcDisconnectMessage, ViewportMessage, WebRtcDisconnectMessage,
WebRtcSignalToClientMessage, WebRtcSignalToClientMessage,
WebRtcSignalToServerMessage, WebRtcStartMessage WebRtcSignalToServerMessage, WebRtcStartMessage, QueryJitsiJwtMessage, SendJitsiJwtMessage
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import {PointInterface} from "../Model/Websocket/PointInterface"; import {PointInterface} from "../Model/Websocket/PointInterface";
import {User} from "../Model/User"; import {User} from "../Model/User";
@ -27,20 +27,22 @@ import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils";
import {Group} from "../Model/Group"; import {Group} from "../Model/Group";
import {cpuTracker} from "./CpuTracker"; import {cpuTracker} from "./CpuTracker";
import {isSetPlayerDetailsMessage} from "../Model/Websocket/SetPlayerDetailsMessage"; import {isSetPlayerDetailsMessage} from "../Model/Websocket/SetPlayerDetailsMessage";
import {GROUP_RADIUS, MINIMUM_DISTANCE} from "../Enum/EnvironmentVariable"; import {GROUP_RADIUS, JITSI_ISS, MINIMUM_DISTANCE, SECRET_JITSI_KEY} from "../Enum/EnvironmentVariable";
import {Movable} from "../Model/Movable"; import {Movable} from "../Model/Movable";
import {PositionInterface} from "../Model/PositionInterface"; import {PositionInterface} from "../Model/PositionInterface";
import {adminApi} from "./AdminApi"; import {adminApi} from "./AdminApi";
import Direction = PositionMessage.Direction; import Direction = PositionMessage.Direction;
import {Gauge} from "prom-client"; import {Gauge} from "prom-client";
import {emitError, emitInBatch} from "./IoSocketHelpers"; import {emitError, emitInBatch} from "./IoSocketHelpers";
import Jwt from "jsonwebtoken";
import {JITSI_URL} from "../Enum/EnvironmentVariable";
class SocketManager { class SocketManager {
private Worlds: Map<string, GameRoom> = new Map<string, GameRoom>(); private Worlds: Map<string, GameRoom> = new Map<string, GameRoom>();
private sockets: Map<number, ExSocketInterface> = new Map<number, ExSocketInterface>(); private sockets: Map<number, ExSocketInterface> = new Map<number, ExSocketInterface>();
private nbClientsGauge: Gauge<string>; private nbClientsGauge: Gauge<string>;
private nbClientsPerRoomGauge: Gauge<string>; private nbClientsPerRoomGauge: Gauge<string>;
constructor() { constructor() {
this.nbClientsGauge = new Gauge({ this.nbClientsGauge = new Gauge({
name: 'workadventure_nb_sockets', name: 'workadventure_nb_sockets',
@ -55,14 +57,14 @@ class SocketManager {
} }
handleJoinRoom(client: ExSocketInterface): void { handleJoinRoom(client: ExSocketInterface): void {
const position = client.position; const position = client.position;
const viewport = client.viewport; const viewport = client.viewport;
try { try {
this.sockets.set(client.userId, client); //todo: should this be at the end of the function? this.sockets.set(client.userId, client); //todo: should this be at the end of the function?
this.nbClientsGauge.inc(); this.nbClientsGauge.inc();
// Let's log server load when a user joins // Let's log server load when a user joins
console.log(new Date().toISOString() + ' A user joined (', socketManager.sockets.size, ' connected users)'); console.log(new Date().toISOString() + ' A user joined (', socketManager.sockets.size, ' connected users)');
//join new previous room //join new previous room
const gameRoom = this.joinRoom(client, position); const gameRoom = this.joinRoom(client, position);
@ -592,7 +594,44 @@ class SocketManager {
} }
return null; return null;
} }
public handleQueryJitsiJwtMessage(client: ExSocketInterface, queryJitsiJwtMessage: QueryJitsiJwtMessage) {
const room = queryJitsiJwtMessage.getJitsiroom();
const tag = queryJitsiJwtMessage.getTag(); // FIXME: this is not secure. We should load the JSON for the current room and check rights associated to room instead.
if (SECRET_JITSI_KEY === '') {
throw new Error('You must set the SECRET_JITSI_KEY key to the secret to generate JWT tokens for Jitsi.');
}
// Let's see if the current client has
const isAdmin = client.tags.includes(tag);
const jwt = Jwt.sign({
"aud": "jitsi",
"iss": JITSI_ISS,
"sub": JITSI_URL,
"room": room,
"moderator": isAdmin
}, SECRET_JITSI_KEY, {
expiresIn: '1d',
algorithm: "HS256",
header:
{
"alg": "HS256",
"typ": "JWT"
}
});
const sendJitsiJwtMessage = new SendJitsiJwtMessage();
sendJitsiJwtMessage.setJitsiroom(room);
sendJitsiJwtMessage.setJwt(jwt);
const serverToClientMessage = new ServerToClientMessage();
serverToClientMessage.setSendjitsijwtmessage(sendJitsiJwtMessage);
client.send(serverToClientMessage.serializeBinary().buffer, true);
}
} }
export const socketManager = new SocketManager(); export const socketManager = new SocketManager();

View file

@ -16,7 +16,10 @@
"env": { "env": {
"SECRET_KEY": "tempSecretKeyNeedsToChange", "SECRET_KEY": "tempSecretKeyNeedsToChange",
"ADMIN_API_TOKEN": env.ADMIN_API_TOKEN, "ADMIN_API_TOKEN": env.ADMIN_API_TOKEN,
"ADMIN_API_URL": "https://admin."+url "ADMIN_API_URL": "https://admin."+url,
"JITSI_ISS": env.JITSI_ISS,
"JITSI_URL": env.JITSI_URL,
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
} }
}, },
"front": { "front": {
@ -28,10 +31,12 @@
"ports": [80], "ports": [80],
"env": { "env": {
"API_URL": "api."+url, "API_URL": "api."+url,
"JITSI_URL": "meet.jit.si", "JITSI_URL": env.JITSI_URL,
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
"TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443", "TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443",
"TURN_USER": "workadventure", "TURN_USER": "workadventure",
"TURN_PASSWORD": "WorkAdventure123" "TURN_PASSWORD": "WorkAdventure123",
"JITSI_PRIVATE_MODE": if env.SECRET_JITSI_KEY != '' then "true" else "false"
} }
}, },
"maps": { "maps": {

View file

@ -23,6 +23,7 @@ services:
environment: environment:
DEBUG_MODE: "$DEBUG_MODE" DEBUG_MODE: "$DEBUG_MODE"
JITSI_URL: $JITSI_URL JITSI_URL: $JITSI_URL
JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE"
HOST: "0.0.0.0" HOST: "0.0.0.0"
NODE_ENV: development NODE_ENV: development
API_URL: api.workadventure.localhost API_URL: api.workadventure.localhost
@ -72,8 +73,11 @@ services:
environment: environment:
STARTUP_COMMAND_1: yarn install STARTUP_COMMAND_1: yarn install
SECRET_KEY: yourSecretKey SECRET_KEY: yourSecretKey
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
ALLOW_ARTILLERY: "true" ALLOW_ARTILLERY: "true"
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN" ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
JITSI_URL: $JITSI_URL
JITSI_ISS: $JITSI_ISS
volumes: volumes:
- ./back:/usr/src/app - ./back:/usr/src/app
labels: labels:

View file

@ -27,6 +27,7 @@ export enum EventMessage{
STOP_GLOBAL_MESSAGE = "stop-global-message", STOP_GLOBAL_MESSAGE = "stop-global-message",
TELEPORT = "teleport", TELEPORT = "teleport",
START_JITSI_ROOM = "start-jitsi-room",
} }
export interface PointInterface { export interface PointInterface {

View file

@ -22,7 +22,7 @@ import {
WebRtcSignalToServerMessage, WebRtcSignalToServerMessage,
WebRtcStartMessage, WebRtcStartMessage,
ReportPlayerMessage, ReportPlayerMessage,
TeleportMessageMessage TeleportMessageMessage, QueryJitsiJwtMessage, SendJitsiJwtMessage
} from "../Messages/generated/messages_pb" } from "../Messages/generated/messages_pb"
import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import {UserSimplePeerInterface} from "../WebRtc/SimplePeer";
@ -150,6 +150,8 @@ export class RoomConnection implements RoomConnection {
this.dispatch(EventMessage.STOP_GLOBAL_MESSAGE, message.getStopglobalmessage()); this.dispatch(EventMessage.STOP_GLOBAL_MESSAGE, message.getStopglobalmessage());
} else if (message.hasTeleportmessagemessage()) { } else if (message.hasTeleportmessagemessage()) {
this.dispatch(EventMessage.TELEPORT, message.getTeleportmessagemessage()); this.dispatch(EventMessage.TELEPORT, message.getTeleportmessagemessage());
} else if (message.hasSendjitsijwtmessage()) {
this.dispatch(EventMessage.START_JITSI_ROOM, message.getSendjitsijwtmessage());
} else { } else {
throw new Error('Unknown message received'); throw new Error('Unknown message received');
} }
@ -501,6 +503,25 @@ export class RoomConnection implements RoomConnection {
this.socket.send(clientToServerMessage.serializeBinary().buffer); this.socket.send(clientToServerMessage.serializeBinary().buffer);
} }
public emitQueryJitsiJwtMessage(jitsiRoom: string, tag: string|undefined ): void {
const queryJitsiJwtMessage = new QueryJitsiJwtMessage();
queryJitsiJwtMessage.setJitsiroom(jitsiRoom);
if (tag !== undefined) {
queryJitsiJwtMessage.setTag(tag);
}
const clientToServerMessage = new ClientToServerMessage();
clientToServerMessage.setQueryjitsijwtmessage(queryJitsiJwtMessage);
this.socket.send(clientToServerMessage.serializeBinary().buffer);
}
public onStartJitsiRoom(callback: (jwt: string, room: string) => void): void {
this.onMessage(EventMessage.START_JITSI_ROOM, (message: SendJitsiJwtMessage) => {
callback(message.getJwt(), message.getJitsiroom());
});
}
public hasTag(tag: string): boolean { public hasTag(tag: string): boolean {
return this.tags.includes(tag); return this.tags.includes(tag);
} }

View file

@ -4,6 +4,7 @@ const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca";
const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com'; const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com';
const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$'; const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$';
const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL;
const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true";
const RESOLUTION = 3; const RESOLUTION = 3;
const ZOOM_LEVEL = 1/*3/4*/; const ZOOM_LEVEL = 1/*3/4*/;
const POSITION_DELAY = 200; // Wait 200ms between sending position events const POSITION_DELAY = 200; // Wait 200ms between sending position events
@ -19,5 +20,6 @@ export {
TURN_SERVER, TURN_SERVER,
TURN_USER, TURN_USER,
TURN_PASSWORD, TURN_PASSWORD,
JITSI_URL JITSI_URL,
JITSI_PRIVATE_MODE
} }

View file

@ -1,6 +1,6 @@
import {ITiledMap} from "../Map/ITiledMap"; import {ITiledMap} from "../Map/ITiledMap";
export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined) => void; export type PropertyChangeCallback = (newValue: string | number | boolean | undefined, oldValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) => void;
/** /**
* A wrapper around a ITiledMap interface to provide additional capabilities. * A wrapper around a ITiledMap interface to provide additional capabilities.
@ -35,14 +35,14 @@ export class GameMap {
for (const [newPropName, newPropValue] of newProps.entries()) { for (const [newPropName, newPropValue] of newProps.entries()) {
const oldPropValue = oldProps.get(newPropName); const oldPropValue = oldProps.get(newPropName);
if (oldPropValue !== newPropValue) { if (oldPropValue !== newPropValue) {
this.trigger(newPropName, oldPropValue, newPropValue); this.trigger(newPropName, oldPropValue, newPropValue, newProps);
} }
} }
for (const [oldPropName, oldPropValue] of oldProps.entries()) { for (const [oldPropName, oldPropValue] of oldProps.entries()) {
if (!newProps.has(oldPropName)) { if (!newProps.has(oldPropName)) {
// We found a property that disappeared // We found a property that disappeared
this.trigger(oldPropName, oldPropValue, undefined); this.trigger(oldPropName, oldPropValue, undefined, newProps);
} }
} }
@ -74,11 +74,11 @@ export class GameMap {
return properties; return properties;
} }
private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined) { private trigger(propName: string, oldValue: string | number | boolean | undefined, newValue: string | number | boolean | undefined, allProps: Map<string, string | boolean | number>) {
const callbacksArray = this.callbacks.get(propName); const callbacksArray = this.callbacks.get(propName);
if (callbacksArray !== undefined) { if (callbacksArray !== undefined) {
for (const callback of callbacksArray) { for (const callback of callbacksArray) {
callback(newValue, oldValue); callback(newValue, oldValue, allProps);
} }
} }
} }

View file

@ -9,7 +9,14 @@ import {
RoomJoinedMessageInterface RoomJoinedMessageInterface
} from "../../Connexion/ConnexionModels"; } from "../../Connexion/ConnexionModels";
import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player"; import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player";
import {DEBUG_MODE, JITSI_URL, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; import {
DEBUG_MODE,
JITSI_PRIVATE_MODE,
JITSI_URL,
POSITION_DELAY,
RESOLUTION,
ZOOM_LEVEL
} from "../../Enum/EnvironmentVariable";
import { import {
ITiledMap, ITiledMap,
ITiledMapLayer, ITiledMapLayer,
@ -137,6 +144,8 @@ export class GameScene extends ResizableScene implements CenterListener {
private outlinedItem: ActionableItem|null = null; private outlinedItem: ActionableItem|null = null;
private userInputManager!: UserInputManager; private userInputManager!: UserInputManager;
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any
static createFromUrl(room: Room, mapUrlFile: string, gameSceneKey: string|null = null): GameScene { static createFromUrl(room: Room, mapUrlFile: string, gameSceneKey: string|null = null): GameScene {
// We use the map URL as a key // We use the map URL as a key
if (gameSceneKey === null) { if (gameSceneKey === null) {
@ -460,34 +469,18 @@ export class GameScene extends ResizableScene implements CenterListener {
CoWebsiteManager.loadCoWebsite(newValue as string); CoWebsiteManager.loadCoWebsite(newValue as string);
} }
}); });
let jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => {
this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue) => {
if (newValue === undefined) { if (newValue === undefined) {
this.connection.setSilent(false); this.stopJitsi();
jitsiApi?.dispose();
CoWebsiteManager.closeCoWebsite();
mediaManager.showGameOverlay();
} else { } else {
CoWebsiteManager.insertCoWebsite((cowebsiteDiv => { console.log("JITSI_PRIVATE_MODE", JITSI_PRIVATE_MODE);
const domain = JITSI_URL; if (JITSI_PRIVATE_MODE) {
const options = { const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined;
roomName: this.instance + "-" + newValue,
width: "100%", this.connection.emitQueryJitsiJwtMessage(this.instance + "-" + newValue, adminTag);
height: "100%", } else {
parentNode: cowebsiteDiv, this.startJitsi(newValue as string);
configOverwrite: { }
prejoinPageEnabled: false
},
interfaceConfigOverwrite: {
SHOW_CHROME_EXTENSION_BANNER: false,
MOBILE_APP_PROMO: false
}
};
jitsiApi = new (window as any).JitsiMeetExternalAPI(domain, options); // eslint-disable-line @typescript-eslint/no-explicit-any
jitsiApi.executeCommand('displayName', gameManager.getPlayerName());
}));
this.connection.setSilent(true);
mediaManager.hideGameOverlay();
} }
}) })
@ -597,6 +590,13 @@ export class GameScene extends ResizableScene implements CenterListener {
item.fire(message.event, message.state, message.parameters); item.fire(message.event, message.state, message.parameters);
})); }));
/**
* Triggered when we receive the JWT token to connect to Jitsi
*/
connection.onStartJitsiRoom((jwt, room) => {
this.startJitsi(room, jwt);
});
// When connection is performed, let's connect SimplePeer // When connection is performed, let's connect SimplePeer
this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic); this.simplePeer = new SimplePeer(this.connection, !this.room.isPublic);
this.GlobalMessageManager = new GlobalMessageManager(this.connection); this.GlobalMessageManager = new GlobalMessageManager(this.connection);
@ -1191,4 +1191,55 @@ export class GameScene extends ResizableScene implements CenterListener {
public onCenterChange(): void { public onCenterChange(): void {
this.updateCameraOffset(); this.updateCameraOffset();
} }
public startJitsi(roomName: string, jwt?: string): void {
CoWebsiteManager.insertCoWebsite((cowebsiteDiv => {
const domain = JITSI_URL;
const options = {
roomName: roomName,
jwt: jwt,
width: "100%",
height: "100%",
parentNode: cowebsiteDiv,
configOverwrite: {
prejoinPageEnabled: false
},
interfaceConfigOverwrite: {
SHOW_CHROME_EXTENSION_BANNER: false,
MOBILE_APP_PROMO: false,
HIDE_INVITE_MORE_HEADER: true,
// Note: hiding brand does not seem to work, we probably need to put this on the server side.
SHOW_BRAND_WATERMARK: false,
SHOW_JITSI_WATERMARK: false,
SHOW_POWERED_BY: false,
SHOW_PROMOTIONAL_CLOSE_PAGE: false,
SHOW_WATERMARK_FOR_GUESTS: false,
TOOLBAR_BUTTONS: [
'microphone', 'camera', 'closedcaptions', 'desktop', /*'embedmeeting',*/ 'fullscreen',
'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
'videoquality', 'filmstrip', /*'invite',*/ 'feedback', 'stats', 'shortcuts',
'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', /*'security'*/
],
}
};
if (!options.jwt) {
delete options.jwt;
}
this.jitsiApi = new (window as any).JitsiMeetExternalAPI(domain, options); // eslint-disable-line @typescript-eslint/no-explicit-any
this.jitsiApi.executeCommand('displayName', gameManager.getPlayerName());
}));
this.connection.setSilent(true);
mediaManager.hideGameOverlay();
}
public stopJitsi(): void {
this.connection.setSilent(false);
this.jitsiApi?.dispose();
CoWebsiteManager.closeCoWebsite();
mediaManager.showGameOverlay();
}
} }

View file

@ -45,7 +45,7 @@ module.exports = {
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Phaser: 'phaser' Phaser: 'phaser'
}), }),
new webpack.EnvironmentPlugin(['API_URL', 'DEBUG_MODE', 'TURN_SERVER', 'TURN_USER', 'TURN_PASSWORD', 'JITSI_URL']) new webpack.EnvironmentPlugin(['API_URL', 'DEBUG_MODE', 'TURN_SERVER', 'TURN_USER', 'TURN_PASSWORD', 'JITSI_URL', 'JITSI_PRIVATE_MODE'])
], ],
}; };

View file

@ -53,6 +53,11 @@ message ReportPlayerMessage {
string reportComment = 2; string reportComment = 2;
} }
message QueryJitsiJwtMessage {
string jitsiRoom = 1;
string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map!
}
message ClientToServerMessage { message ClientToServerMessage {
oneof message { oneof message {
UserMovesMessage userMovesMessage = 2; UserMovesMessage userMovesMessage = 2;
@ -65,6 +70,7 @@ message ClientToServerMessage {
PlayGlobalMessage playGlobalMessage = 9; PlayGlobalMessage playGlobalMessage = 9;
StopGlobalMessage stopGlobalMessage = 10; StopGlobalMessage stopGlobalMessage = 10;
ReportPlayerMessage reportPlayerMessage = 11; ReportPlayerMessage reportPlayerMessage = 11;
QueryJitsiJwtMessage queryJitsiJwtMessage = 12;
} }
} }
@ -167,6 +173,11 @@ message TeleportMessageMessage{
string map = 1; string map = 1;
} }
message SendJitsiJwtMessage {
string jitsiRoom = 1;
string jwt = 2;
}
message ServerToClientMessage { message ServerToClientMessage {
oneof message { oneof message {
BatchMessage batchMessage = 1; BatchMessage batchMessage = 1;
@ -179,5 +190,6 @@ message ServerToClientMessage {
PlayGlobalMessage playGlobalMessage = 8; PlayGlobalMessage playGlobalMessage = 8;
StopGlobalMessage stopGlobalMessage = 9; StopGlobalMessage stopGlobalMessage = 9;
TeleportMessageMessage teleportMessageMessage = 10; TeleportMessageMessage teleportMessageMessage = 10;
SendJitsiJwtMessage sendJitsiJwtMessage = 11;
} }
} }