[mirotalk] - #209 add JWT, update dep
This commit is contained in:
+5
-3
@@ -10,7 +10,6 @@ HOST=localhost
|
||||
# Access to the instance is restricted to only the specified IP addresses in the allowed list. This feature is disabled by default.
|
||||
|
||||
IP_WHITELIST_ENABLED=false # true or false
|
||||
|
||||
IP_WHITELIST_ALLOWED='["127.0.0.1", "::1"]'
|
||||
|
||||
# Host protection
|
||||
@@ -19,11 +18,14 @@ IP_WHITELIST_ALLOWED='["127.0.0.1", "::1"]'
|
||||
# HOST_USERS: This is the list of valid host users along with their credentials.
|
||||
|
||||
HOST_PROTECTED=false # true or false
|
||||
|
||||
HOST_USER_AUTH=false # true or false
|
||||
|
||||
HOST_USERS='[{"username": "username", "password": "password"},{"username": "username2", "password": "password2"}]'
|
||||
|
||||
# JWT token config
|
||||
|
||||
JWT_KEY=mirotalk_jwt_secret
|
||||
JWT_EXP=1h
|
||||
|
||||
# Presenters list
|
||||
# In our virtual room, the first participant to join will assume the role of the presenter.
|
||||
# Additionally, we have the option to include more presenters and co-presenters, each identified by their username.
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
- Host protection to prevent unauthorized access.
|
||||
- User auth to prevent unauthorized access.
|
||||
- Room password protection.
|
||||
- JWT.io securely manages credentials for host configurations and user authentication, enhancing security and streamlining processes.
|
||||
- Compatible with desktop and mobile devices.
|
||||
- Optimized mobile room URL sharing.
|
||||
- Webcam streaming with front and rear camera support for mobile devices.
|
||||
@@ -95,17 +96,16 @@
|
||||
- https://p2p.mirotalk.com/join?room=test&name=mirotalk&audio=0&video=0&screen=0&hide=0¬ify=0
|
||||
- https://mirotalk.up.railway.app/join?room=test&name=mirotalk&audio=0&video=0&screen=0&hide=0¬ify=0
|
||||
|
||||
| Params | Type | Description |
|
||||
| -------- | ------- | --------------- |
|
||||
| room | string | Room Id |
|
||||
| name | string | User name |
|
||||
| audio | boolean | Audio stream |
|
||||
| video | boolean | Video stream |
|
||||
| screen | boolean | Screen stream |
|
||||
| hide | boolean | Hide myself |
|
||||
| notify | boolean | Welcome message |
|
||||
| username | string | auth username |
|
||||
| password | string | auth password |
|
||||
| Params | Type | Description |
|
||||
| ------ | ------- | --------------- |
|
||||
| room | string | Room Id |
|
||||
| name | string | User name |
|
||||
| audio | boolean | Audio stream |
|
||||
| video | boolean | Video stream |
|
||||
| screen | boolean | Screen stream |
|
||||
| hide | boolean | Hide myself |
|
||||
| notify | boolean | Welcome message |
|
||||
| token | string | jwt token |
|
||||
|
||||
> **Note**
|
||||
>
|
||||
@@ -224,6 +224,10 @@ $ docker-compose down
|
||||
$ curl -X POST "http://localhost:3000/api/v1/join" -H "authorization: mirotalk_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true"}'
|
||||
$ curl -X POST "https://p2p.mirotalk.com/api/v1/join" -H "authorization: mirotalk_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true"}'
|
||||
$ curl -X POST "https://mirotalk.up.railway.app/api/v1/join" -H "authorization: mirotalk_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true"}'
|
||||
# The response will give you an entry point/URL for direct joining to the meeting with a token.
|
||||
$ curl -X POST "http://localhost:3000/api/v1/join" -H "authorization: mirotalk_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}'
|
||||
$ curl -X POST "https://p2p.mirotalk.com/api/v1/join" -H "authorization: mirotalk_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}'
|
||||
$ curl -X POST "https://mirotalk.up.railway.app/api/v1/join" -H "authorization: mirotalk_default_secret" -H "Content-Type: application/json" --data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -26,6 +26,12 @@ async function getJoin() {
|
||||
screen: false,
|
||||
hide: false,
|
||||
notify: true,
|
||||
token: {
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
presenter: true,
|
||||
expire: '1h',
|
||||
},
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
@@ -25,6 +25,12 @@ $data = array(
|
||||
"screen" => false,
|
||||
"hide" => false,
|
||||
"notify" => true,
|
||||
"token" => array(
|
||||
"username" => "username",
|
||||
"password" => "password",
|
||||
"presenter" => true,
|
||||
"expire" => "1h",
|
||||
),
|
||||
);
|
||||
$data_string = json_encode($data);
|
||||
|
||||
|
||||
@@ -20,6 +20,12 @@ data = {
|
||||
"screen": "false",
|
||||
"hide": "false",
|
||||
"notify": "true",
|
||||
"token": {
|
||||
"username": "username",
|
||||
"password": "password",
|
||||
"presenter": "true",
|
||||
"expire": "1h",
|
||||
}
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
|
||||
@@ -8,5 +8,5 @@ MIROTALK_URL="https://p2p.mirotalk.com/api/v1/join"
|
||||
curl $MIROTALK_URL \
|
||||
--header "authorization: $API_KEY_SECRET" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true"}' \
|
||||
--data '{"room":"test","name":"mirotalk","audio":"true","video":"true","screen":"false","hide":"false","notify":"true","token":{"username":"username","password":"password","presenter":"true", "expire":"1h"}}' \
|
||||
--request POST
|
||||
+25
-7
@@ -1,7 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { v4: uuidV4 } = require('uuid');
|
||||
|
||||
const JWT_KEY = process.env.JWT_KEY || 'mirotalk_jwt_secret';
|
||||
const JWT_EXP = process.env.JWT_EXP || '1h';
|
||||
|
||||
module.exports = class ServerApi {
|
||||
constructor(host = null, authorization = null, api_key_secret = null) {
|
||||
this._host = host;
|
||||
@@ -19,23 +23,37 @@ module.exports = class ServerApi {
|
||||
}
|
||||
|
||||
getJoinURL(data) {
|
||||
// Get data...
|
||||
const { room, name, audio, video, screen, hide, notify, token } = data;
|
||||
|
||||
let jwtToken = '';
|
||||
|
||||
if (token) {
|
||||
const { username, password, presenter, expire } = token;
|
||||
jwtToken =
|
||||
'&token=' +
|
||||
jwt.sign({ username: username, password: password, presenter: presenter }, JWT_KEY, {
|
||||
expiresIn: expire ? expire : JWT_EXP,
|
||||
});
|
||||
}
|
||||
return (
|
||||
this.getProtocol() +
|
||||
this._host +
|
||||
'/join?room=' +
|
||||
data.room +
|
||||
room +
|
||||
'&name=' +
|
||||
data.name +
|
||||
name +
|
||||
'&audio=' +
|
||||
data.audio +
|
||||
audio +
|
||||
'&video=' +
|
||||
data.video +
|
||||
video +
|
||||
'&screen=' +
|
||||
data.screen +
|
||||
screen +
|
||||
'&hide=' +
|
||||
data.hide +
|
||||
hide +
|
||||
'¬ify=' +
|
||||
data.notify
|
||||
notify +
|
||||
jwtToken
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+76
-23
@@ -18,6 +18,7 @@ dependencies: {
|
||||
crypto-js : https://www.npmjs.com/package/crypto-js
|
||||
dotenv : https://www.npmjs.com/package/dotenv
|
||||
express : https://www.npmjs.com/package/express
|
||||
jsonwebtoken : https://www.npmjs.com/package/jsonwebtoken
|
||||
ngrok : https://www.npmjs.com/package/ngrok
|
||||
qs : https://www.npmjs.com/package/qs
|
||||
openai : https://www.npmjs.com/package/openai
|
||||
@@ -38,7 +39,7 @@ dependencies: {
|
||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.2.84
|
||||
* @version 1.2.85
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -54,6 +55,7 @@ const express = require('express');
|
||||
const cors = require('cors');
|
||||
const path = require('path');
|
||||
const axios = require('axios');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const app = express();
|
||||
const checkXSS = require('./xss.js');
|
||||
const ServerApi = require('./api');
|
||||
@@ -102,6 +104,12 @@ const hostCfg = {
|
||||
authenticated: !hostProtected,
|
||||
};
|
||||
|
||||
// JWT config
|
||||
const jwtCfg = {
|
||||
JWT_KEY: process.env.JWT_KEY || 'mirotalk_jwt_secret',
|
||||
JWT_EXP: process.env.JWT_EXP || '1h',
|
||||
};
|
||||
|
||||
// Room presenters
|
||||
const roomPresentersString = process.env.PRESENTERS || '["MiroTalk P2P"]';
|
||||
const roomPresenters = JSON.parse(roomPresentersString);
|
||||
@@ -355,24 +363,43 @@ app.get('/join/', (req, res) => {
|
||||
if (Object.keys(req.query).length > 0) {
|
||||
log.debug('Request Query', req.query);
|
||||
/*
|
||||
http://localhost:3000/join?room=test&name=mirotalk&audio=1&video=1&screen=0¬ify=0&hide=1&username=username&password=password
|
||||
http://localhost:3000/join?room=test&name=mirotalk&audio=1&video=1&screen=0¬ify=0&hide=1&token=token
|
||||
https://p2p.mirotalk.com/join?room=test&name=mirotalk&audio=1&video=1&screen=0¬ify=0&hide=0
|
||||
https://mirotalk.up.railway.app/join?room=test&name=mirotalk&audio=1&video=1&screen=0¬ify=0&hide=0
|
||||
*/
|
||||
const { room, name, audio, video, screen, notify, hide, username, password } = checkXSS(req.query);
|
||||
const { room, name, audio, video, screen, notify, hide, token } = checkXSS(req.query);
|
||||
|
||||
// check if valid peer
|
||||
const isPeerValid = isAuthPeer(username, password);
|
||||
let peerUsername,
|
||||
peerPassword = '';
|
||||
let isPeerValid = false;
|
||||
let isPeerPresenter = false;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const { username, password, presenter } = checkXSS(jwt.verify(token, jwtCfg.JWT_KEY));
|
||||
// Peer credentials
|
||||
peerUsername = username;
|
||||
peerPassword = password;
|
||||
// Check if valid peer
|
||||
isPeerValid = isAuthPeer(username, password);
|
||||
// Check if presenter
|
||||
isPeerPresenter = presenter === '1' || presenter === 'true';
|
||||
} catch (err) {
|
||||
// Invalid token
|
||||
log.error('Direct Join JWT error', err.message);
|
||||
return hostCfg.protected || hostCfg.user_auth ? res.sendFile(views.login) : res.sendFile(views.landing);
|
||||
}
|
||||
}
|
||||
|
||||
// Peer valid going to auth as host
|
||||
if (hostCfg.protected && isPeerValid && !hostCfg.authenticated) {
|
||||
if (hostCfg.protected && isPeerValid && isPeerPresenter && !hostCfg.authenticated) {
|
||||
const ip = getIP(req);
|
||||
hostCfg.authenticated = true;
|
||||
authHost = new Host(ip, true);
|
||||
log.debug('Direct Join user auth as host done', {
|
||||
ip: ip,
|
||||
username: username,
|
||||
password: password,
|
||||
username: peerUsername,
|
||||
password: peerPassword,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -442,13 +469,19 @@ app.post(['/login'], (req, res) => {
|
||||
hostCfg.authenticated = true;
|
||||
authHost = new Host(ip, true);
|
||||
log.debug('HOST LOGIN OK', { ip: ip, authorized: authHost.isAuthorized(ip) });
|
||||
return res.status(200).json({ message: 'authorized' });
|
||||
const token = jwt.sign({ username: username, password: password, presenter: true }, jwtCfg.JWT_KEY, {
|
||||
expiresIn: jwtCfg.JWT_EXP,
|
||||
});
|
||||
return res.status(200).json({ message: token });
|
||||
}
|
||||
|
||||
// Peer auth valid
|
||||
if (isPeerValid) {
|
||||
log.debug('PEER LOGIN OK', { ip: ip, authorized: true });
|
||||
return res.status(200).json({ message: 'authorized' });
|
||||
const token = jwt.sign({ username: username, password: password, presenter: false }, jwtCfg.JWT_KEY, {
|
||||
expiresIn: jwtCfg.JWT_EXP,
|
||||
});
|
||||
return res.status(200).json({ message: token });
|
||||
} else {
|
||||
return res.status(401).json({ message: 'unauthorized' });
|
||||
}
|
||||
@@ -573,6 +606,7 @@ async function ngrokStart() {
|
||||
iceServers: iceServers,
|
||||
stats: statsData,
|
||||
host: hostCfg,
|
||||
jwtCfg: jwtCfg,
|
||||
presenters: roomPresenters,
|
||||
ip_whitelist: ipWhitelist,
|
||||
ngrok: {
|
||||
@@ -630,6 +664,7 @@ server.listen(port, null, () => {
|
||||
iceServers: iceServers,
|
||||
stats: statsData,
|
||||
host: hostCfg,
|
||||
jwtCfg: jwtCfg,
|
||||
presenters: roomPresenters,
|
||||
ip_whitelist: ipWhitelist,
|
||||
server: host,
|
||||
@@ -784,8 +819,7 @@ io.sockets.on('connect', async (socket) => {
|
||||
channel_password,
|
||||
peer_uuid,
|
||||
peer_name,
|
||||
peer_username,
|
||||
peer_password,
|
||||
peer_token,
|
||||
peer_video,
|
||||
peer_audio,
|
||||
peer_video_status,
|
||||
@@ -808,18 +842,37 @@ io.sockets.on('connect', async (socket) => {
|
||||
// no presenter aka host in presenters init
|
||||
if (!(channel in presenters)) presenters[channel] = {};
|
||||
|
||||
let is_presenter = true;
|
||||
|
||||
// User Auth required, we check if peer valid
|
||||
if (hostCfg.user_auth) {
|
||||
const isPeerValid = isAuthPeer(peer_username, peer_password);
|
||||
// Check JWT
|
||||
if (peer_token) {
|
||||
try {
|
||||
const { username, password, presenter } = checkXSS(jwt.verify(peer_token, jwtCfg.JWT_KEY));
|
||||
|
||||
log.debug('[' + socket.id + '] JOIN ROOM - HOST PROTECTED - USER AUTH check peer', {
|
||||
ip: peer_ip,
|
||||
peer_username: peer_username,
|
||||
peer_password: peer_password,
|
||||
peer_valid: isPeerValid,
|
||||
});
|
||||
const isPeerValid = isAuthPeer(username, password);
|
||||
|
||||
if (!isPeerValid) {
|
||||
is_presenter = presenter === '1' || presenter === 'true';
|
||||
|
||||
log.debug('[' + socket.id + '] JOIN ROOM - USER AUTH check peer', {
|
||||
ip: peer_ip,
|
||||
peer_username: username,
|
||||
peer_password: password,
|
||||
peer_valid: isPeerValid,
|
||||
peer_presenter: is_presenter,
|
||||
});
|
||||
|
||||
if (!isPeerValid) {
|
||||
// redirect peer to login page
|
||||
return socket.emit('unauthorized');
|
||||
}
|
||||
} catch (err) {
|
||||
// redirect peer to login page
|
||||
log.error('[' + socket.id + '] [Warning] Join Room JWT error', err.message);
|
||||
return socket.emit('unauthorized');
|
||||
}
|
||||
} else {
|
||||
// redirect peer to login page
|
||||
return socket.emit('unauthorized');
|
||||
}
|
||||
@@ -836,7 +889,7 @@ io.sockets.on('connect', async (socket) => {
|
||||
peer_ip: peer_ip,
|
||||
peer_name: peer_name,
|
||||
peer_uuid: peer_uuid,
|
||||
is_presenter: true,
|
||||
is_presenter: is_presenter,
|
||||
};
|
||||
// first we check if the username match the presenters username
|
||||
if (roomPresenters && roomPresenters.includes(peer_name)) {
|
||||
@@ -848,8 +901,8 @@ io.sockets.on('connect', async (socket) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if peer is presenter
|
||||
const isPresenter = await isPeerPresenter(channel, socket.id, peer_name, peer_uuid);
|
||||
// Check if peer is presenter, if token check the presenter key
|
||||
const isPresenter = peer_token ? is_presenter : await isPeerPresenter(channel, socket.id, peer_name, peer_uuid);
|
||||
|
||||
// collect peers info grp by channels
|
||||
peers[channel][socket.id] = {
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mirotalk",
|
||||
"version": "1.2.84",
|
||||
"version": "1.2.85",
|
||||
"description": "A free WebRTC browser-based video call",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
@@ -45,10 +45,11 @@
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.4.1",
|
||||
"dotenv": "^16.4.2",
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"ngrok": "^4.3.3",
|
||||
"openai": "^4.26.1",
|
||||
"openai": "^4.27.0",
|
||||
"qs": "^6.11.2",
|
||||
"socket.io": "^4.7.4",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
|
||||
+15
-33
@@ -15,7 +15,7 @@
|
||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
||||
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.2.84
|
||||
* @version 1.2.85
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -562,8 +562,7 @@ let isDocumentOnFullScreen = false;
|
||||
let myPeerId; // This socket.id
|
||||
let myPeerUUID = getUUID(); // Unique peer id
|
||||
let myPeerName = getPeerName();
|
||||
let myUsername = getPeerUsername(); // default false if not passed by query params
|
||||
let myPassword = getPeerPassword(); // default false if not passed by query params
|
||||
let myToken = getPeerToken(); // peer JWT
|
||||
let isPresenter = false; // True Who init the room (aka first peer joined)
|
||||
let myHandStatus = false;
|
||||
let myVideoStatus = false;
|
||||
@@ -972,35 +971,19 @@ function getNotify() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Peer username
|
||||
* @returns {mixed} boolean false or username string
|
||||
* Get Peer JWT
|
||||
* @returns {mixed} boolean false or token string
|
||||
*/
|
||||
function getPeerUsername() {
|
||||
if (window.sessionStorage.peer_username) return window.sessionStorage.peer_username;
|
||||
function getPeerToken() {
|
||||
if (window.sessionStorage.peer_token) return window.sessionStorage.peer_token;
|
||||
let qs = new URLSearchParams(window.location.search);
|
||||
let username = filterXSS(qs.get('username'));
|
||||
let queryUsername = false;
|
||||
if (username) {
|
||||
queryUsername = username;
|
||||
let token = filterXSS(qs.get('token'));
|
||||
let queryToken = false;
|
||||
if (token) {
|
||||
queryToken = token;
|
||||
}
|
||||
console.log('Direct join', { username: queryUsername });
|
||||
return queryUsername;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Peer password
|
||||
* @returns {mixed} boolean false or password string
|
||||
*/
|
||||
function getPeerPassword() {
|
||||
if (window.sessionStorage.peer_password) return window.sessionStorage.peer_password;
|
||||
let qs = new URLSearchParams(window.location.search);
|
||||
let password = filterXSS(qs.get('password'));
|
||||
let queryPassword = false;
|
||||
if (password) {
|
||||
queryPassword = password;
|
||||
}
|
||||
console.log('Direct join', { password: queryPassword });
|
||||
return queryPassword;
|
||||
console.log('Direct join', { token: queryToken });
|
||||
return queryToken;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1368,7 +1351,7 @@ async function whoAreYou() {
|
||||
console.log(`11.1 Check if ${myPeerName} exist in the room`, roomId);
|
||||
|
||||
if (await checkUserName()) {
|
||||
return userNameAlreadyInRoom();
|
||||
if (!myToken) return userNameAlreadyInRoom(); // #209 Hack...
|
||||
}
|
||||
|
||||
checkPeerAudioVideo();
|
||||
@@ -1777,8 +1760,7 @@ async function joinToChannel() {
|
||||
peer_info: peerInfo,
|
||||
peer_uuid: myPeerUUID,
|
||||
peer_name: myPeerName,
|
||||
peer_username: myUsername,
|
||||
peer_password: myPassword,
|
||||
peer_token: myToken,
|
||||
peer_video: useVideo,
|
||||
peer_audio: useAudio,
|
||||
peer_video_status: myVideoStatus,
|
||||
@@ -5458,7 +5440,7 @@ function shareRoomByEmail() {
|
||||
function getRoomURL() {
|
||||
return myRoomUrl;
|
||||
// return isHostProtected && isPeerAuthEnabled
|
||||
// ? window.location.origin + '/join/?room=' + roomId + '&username=' + myUsername + '&password=' + myPassword
|
||||
// ? window.location.origin + '/join/?room=' + roomId + '&token=' + myToken
|
||||
// : myRoomUrl;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,16 +163,21 @@
|
||||
console.log(response);
|
||||
|
||||
// Store in session
|
||||
window.sessionStorage.peer_username = username;
|
||||
window.sessionStorage.peer_password = password;
|
||||
const token = response.data.message;
|
||||
window.sessionStorage.peer_token = token;
|
||||
|
||||
if (room) {
|
||||
return (window.location.href =
|
||||
'/join/' + window.location.search);
|
||||
/*
|
||||
return (window.location.href =
|
||||
'/join/?room=' + room + '&token=' + token); */
|
||||
}
|
||||
|
||||
if (roomPath) {
|
||||
return (window.location.href = '/join/' + roomPath);
|
||||
/*
|
||||
return (window.location.href =
|
||||
'/join/?room=' + roomPath + '&token=' + token); */
|
||||
}
|
||||
|
||||
return (window.location.href = '/logged');
|
||||
|
||||
Reference in New Issue
Block a user