[mirotalksfu] - add host.auth_user

هذا الالتزام موجود في:
Miroslav Pejic
2023-11-23 17:46:13 +01:00
الأصل bf0820c69f
التزام a17e1eafb0
13 ملفات معدلة مع 217 إضافات و57 حذوفات

عرض الملف

@@ -35,6 +35,7 @@
- Unlimited conference rooms with no time limitations.
- Translated into 133 languages.
- Host protection to prevent unauthorized access.
- User auth to prevent unauthorized access.
- Room password protection.
- Compatible with desktop and mobile devices.
- Optimized mobile room URL sharing.
@@ -79,26 +80,76 @@
<br/>
- You can `directly join a room` by using link like:
- https://sfu.mirotalk.com/join?room=test&password=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0
- https://sfu.mirotalk.com/join?room=test&roomPassword=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0
| Params | Type | Description |
| -------- | -------------- | --------------- |
| ------------ | -------------- | --------------- |
| room | string | Room Id |
| password | string/boolean | Room password |
| roomPassword | string/boolean | Room password |
| name | string | User name |
| audio | boolean | Audio stream |
| video | boolean | Video stream |
| screen | boolean | Screen stream |
| notify | boolean | Welcome message |
| username | string | auth username |
| password | string | auth password |
> **Note**
>
> When [host protection is enabled](https://github.com/miroslavpejic85/mirotalksfu/commit/ab21686e9ad4b75e14c3ee020141d61b33111dde#commitcomment-62398736) the host needs to provide a valid username and password as specified in the `app/src/config.js`.
>
> After host authentication, participants can join the room using any of the following URL formats:
>
> - https://sfu.mirotalk.com/join/test (URL path)
> - https://sfu.mirotalk.com/join/?room=test&password=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0 (URL with query parameters for direct join)
> The `username` and `password` parameters are required when either `host.protected` or `host.user_auth` is set to `true` in the `app/src/config.js` file. The valid list of users is defined in the `host.users` configuration.
</details>
<details>
<summary>Host Protection Configuration</summary>
<br/>
When [host.protected](https://github.com/miroslavpejic85/mirotalksfu/commit/ab21686e9ad4b75e14c3ee020141d61b33111dde#commitcomment-62398736) or `host.user_auth` is enabled, the host/users must provide a valid username and password as specified in the `app/src/config.js` file.
| Params | Value | Description |
| ---------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `host.protected` | `true` if protection is enabled, `false` if not (default false) | Requires the host to provide a valid username and password during room initialization. |
| `host.user_auth` | `true` if user authentication is required, `false` if not (default false). | Determines whether host authentication is required. |
| `host.users` | JSON array with user objects: `{"username": "username", "password": "password"}` | List of valid host users with their credentials. |
Example:
```js
host: {
protected: true,
user_auth: true,
users: [
{
username: 'username',
password: 'password',
},
{
username: 'username2',
password: 'password2',
},
//...
],
},
```
### Room Initialization
To bypass the login page, join the room with URL parameters:
- [https://sfu.mirotalk.com/join/?room=test&username=username&password=password](https://sfu.mirotalk.com/join/?room=test&username=username&password=password)
### Participant Room Entry
If `host.protected` is enabled, participants can join after host authentication using:
- [https://sfu.mirotalk.com/join/test](https://sfu.mirotalk.com/join/test) (URL path)
- [https://sfu.mirotalk.com/join/?room=test&roomPassword=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0](https://sfu.mirotalk.com/join/?room=test&roomPassword=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0) (URL with query parameters)
If `host.user_auth` is enabled, participants can join with mandatory credentials:
- [https://sfu.mirotalk.com/join/?room=test&username=username&password=password](https://sfu.mirotalk.com/join/?room=test&username=username&password=password) (URL path)
- [https://sfu.mirotalk.com/join/?room=test&name=mirotalk&audio=0&video=0&screen=0&notify=0&username=username&password=password](https://sfu.mirotalk.com/join/?room=test&name=mirotalk&audio=0&video=0&screen=0&&notify=0&username=username&password=password) (URL with query parameters)
</details>

عرض الملف

@@ -17,7 +17,7 @@ async function getJoin() {
},
body: JSON.stringify({
room: 'test',
password: false,
roomPassword: false,
name: 'mirotalksfu',
audio: true,
video: true,

عرض الملف

@@ -18,7 +18,7 @@ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = array(
"room" => "test",
"password" => false,
"roomPassword" => false,
"name" => "mirotalksfu",
"audio" => true,
"video" => true,

عرض الملف

@@ -13,7 +13,7 @@ headers = {
data = {
"room": "test",
"password": "false",
"roomPassword": "false",
"name": "mirotalksfu",
"audio": "true",
"video": "true",

عرض الملف

@@ -7,5 +7,5 @@ MIROTALK_URL="https://sfu.mirotalk.com/api/v1/join"
curl $MIROTALK_URL \
--header "authorization: $API_KEY_SECRET" \
--header "Content-Type: application/json" \
--data '{"room":"test","password":"false","name":"mirotalksfu","audio":"true","video":"true","screen":"false","notify":"true"}' \
--data '{"room":"test","roomPassword":"false","name":"mirotalksfu","audio":"true","video":"true","screen":"false","notify":"true"}' \
--request POST

عرض الملف

@@ -40,7 +40,7 @@ dependencies: {
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.2.7
* @version 1.2.8
*
*/
@@ -92,6 +92,7 @@ const host = 'https://' + 'localhost' + ':' + config.server.listen.port; // conf
const hostCfg = {
protected: config.host.protected,
user_auth: config.host.user_auth,
users: config.host.users,
authenticated: !config.host.protected,
};
@@ -269,13 +270,30 @@ function startServer() {
// no room name specified to join || direct join
app.get('/join/', (req, res) => {
if (hostCfg.authenticated && Object.keys(req.query).length > 0) {
if (Object.keys(req.query).length > 0) {
log.debug('Direct Join', req.query);
// http://localhost:3010/join?room=test&password=0&name=mirotalksfu&audio=1&video=1&screen=1&notify=1
const { room, password, name, audio, video, screen, notify, isPresenter } = checkXSS(req.query);
// if (room && password && name && audio && video && screen && notify) {
if (room) {
// http://localhost:3010/join?room=test&roomPassword=0&name=mirotalksfu&audio=1&video=1&screen=0&notify=1
// http://localhost:3010/join?room=test&roomPassword=0&name=mirotalksfu&audio=1&video=1&screen=0&notify=0&username=username&password=password
const { room, roomPassword, name, audio, video, screen, notify, username, password, isPresenter } =
checkXSS(req.query);
const isPeerValid = isAuthPeer(username, password);
if (hostCfg.protected && isPeerValid && !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,
});
}
if (room && (hostCfg.authenticated || isPeerValid)) {
return res.sendFile(views.room);
} else {
return res.sendFile(views.login);
}
}
if (hostCfg.protected) {
@@ -323,6 +341,11 @@ function startServer() {
res.send(stats);
});
// handle login if user_auth enabled
app.get(['/login'], (req, res) => {
res.sendFile(views.login);
});
// handle logged on host protected
app.get(['/logged'], (req, res) => {
const ip = getIP(req);
@@ -340,24 +363,26 @@ function startServer() {
// handle login on host protected
app.post(['/login'], (req, res) => {
if (hostCfg.protected) {
let ip = getIP(req);
const ip = getIP(req);
log.debug(`Request login to host from: ${ip}`, req.body);
const { username, password } = checkXSS(req.body);
const isValidUser =
hostCfg.users && hostCfg.users.some((user) => user.username === username && user.password === password);
if (isValidUser) {
const isPeerValid = isAuthPeer(username, password);
if (hostCfg.protected && isPeerValid && !hostCfg.authenticated) {
const ip = getIP(req);
hostCfg.authenticated = true;
authHost = new Host(ip, true);
log.debug('LOGIN OK', { ip: ip, authorized: authHost.isAuthorized(ip) });
res.status(200).json({ message: 'authorized' });
} else {
log.debug('LOGIN KO', { ip: ip, authorized: false });
hostCfg.authenticated = false;
res.status(401).json({ message: 'unauthorized' });
log.debug('HOST LOGIN OK', { ip: ip, authorized: authHost.isAuthorized(ip) });
return res.status(200).json({ message: 'authorized' });
}
if (isPeerValid) {
log.debug('PEER LOGIN OK', { ip: ip, authorized: true });
return res.status(200).json({ message: 'authorized' });
} else {
res.redirect('/');
return res.status(401).json({ message: 'unauthorized' });
}
});
@@ -896,6 +921,26 @@ function startServer() {
log.debug('User joined', data);
// User Auth required, we check if peer valid
if (hostCfg.user_auth) {
const peer_username = data.peer_info.peer_username;
const peer_password = data.peer_info.peer_password;
const isPeerValid = isAuthPeer(peer_username, peer_password);
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,
});
if (!isPeerValid) {
// redirect peer to login page
return cb('unauthorized');
}
}
const room = roomList.get(socket.room_id);
room.addPeer(new Peer(socket.id, data));
@@ -1348,6 +1393,10 @@ function startServer() {
}
}
function isAuthPeer(username, password) {
return hostCfg.users && hostCfg.users.some((user) => user.username === username && user.password === password);
}
async function getPeerGeoLocation(ip) {
const endpoint = config.IPLookup.getEndpoint(ip);
log.debug('Get peer geo', { ip: ip, endpoint: endpoint });

عرض الملف

@@ -25,8 +25,8 @@ module.exports = class ServerApi {
this._host +
'/join?room=' +
data.room +
'&password=' +
data.password +
'&roomPassword=' +
data.roomPassword +
'&name=' +
data.name +
'&audio=' +

عرض الملف

@@ -39,10 +39,11 @@ module.exports = {
host: {
/*
Host Protection (default: false)
To enhance host security, enable host protection and provide valid
To enhance host security, enable host protection - user auth and provide valid
usernames and passwords in the users array.
*/
protected: false,
user_auth: false,
users: [
{
username: 'username',

عرض الملف

@@ -1,6 +1,6 @@
{
"name": "mirotalksfu",
"version": "1.2.7",
"version": "1.2.8",
"description": "WebRTC SFU browser-based video calls",
"main": "Server.js",
"scripts": {

ثنائية
public/images/forbidden.png Normal file

ملف ثنائي غير معروض.

بعد

العرض:  |  الارتفاع:  |  الحجم: 3.6 KiB

عرض الملف

@@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.2.7
* @version 1.2.8
*
*/
@@ -86,6 +86,8 @@ let room_id = getRoomId();
let room_password = getRoomPassword();
let peer_name = getPeerName();
let peer_uuid = getPeerUUID();
let peer_username = getPeerUsername();
let peer_password = getPeerPassword();
let isScreenAllowed = getScreen();
let notify = getNotify();
isPresenter = isPeerPresenter();
@@ -568,9 +570,33 @@ function getPeerUUID() {
return peer_uuid;
}
function getPeerUsername() {
if (window.sessionStorage.peer_username) return window.sessionStorage.peer_username;
let qs = new URLSearchParams(window.location.search);
let username = filterXSS(qs.get('username'));
let queryUsername = false;
if (username) {
queryUsername = username;
}
console.log('Direct join', { username: queryUsername });
return queryUsername;
}
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;
}
function getRoomPassword() {
let qs = new URLSearchParams(window.location.search);
let roomPassword = filterXSS(qs.get('password'));
let roomPassword = filterXSS(qs.get('roomPassword'));
if (roomPassword) {
let queryNoRoomPassword = roomPassword === '0' || roomPassword === 'false';
if (queryNoRoomPassword) {
@@ -609,6 +635,8 @@ function getPeerInfo() {
peer_uuid: peer_uuid,
peer_id: socket.id,
peer_name: peer_name,
peer_username: peer_username,
peer_password: peer_password,
peer_presenter: isPresenter,
peer_audio: isAudioAllowed,
peer_video: isVideoAllowed,

عرض الملف

@@ -9,7 +9,7 @@
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.2.7
* @version 1.2.8
*
*/
@@ -83,6 +83,7 @@ const image = {
email: '../images/email.png',
chatgpt: '../images/chatgpt.png',
all: '../images/all.png',
forbidden: '../images/forbidden.png',
};
const mediaType = {
@@ -318,6 +319,12 @@ class RoomClient {
.then(
async function (room) {
console.log('##### JOIN ROOM #####', room);
if (room === 'unauthorized') {
console.log(
'00-WARNING ----> Room is Unauthorized for current user, please provide a valid username and password',
);
return this.userUnauthorized();
}
if (room === 'isLocked') {
this.event(_EVENTS.roomLock);
console.log('00-WARNING ----> Room is Locked, Try to unlock by the password');
@@ -4829,6 +4836,24 @@ class RoomClient {
// HANDLE ROOM ACTION
// ####################################################
userUnauthorized() {
this.sound('alert');
Swal.fire({
allowOutsideClick: false,
allowEscapeKey: false,
background: swalBackground,
imageUrl: image.forbidden,
title: 'Oops, Unauthorized',
text: 'The host has user authentication enabled',
confirmButtonText: `Login`,
showClass: { popup: 'animate__animated animate__fadeInDown' },
hideClass: { popup: 'animate__animated animate__fadeOutUp' },
}).then(() => {
// Login required to join room
openURL(`/login/?room=${this.room_id}`);
});
}
unlockTheRoom() {
if (room_password) {
this.RoomPassword = room_password;

عرض الملف

@@ -133,7 +133,9 @@
const password = filterXSS(document.getElementById('password').value);
// http://localhost:3010/join/?room=test
// http://localhost:3010/join/?room=test&password=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0
// http://localhost:3010/join/?room=test&roomPassword=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0
// http://localhost:3010/join/?room=test&roomPassword=0&name=mirotalksfu&audio=0&video=0&screen=0&notify=0&username=username&password=password
const qs = new URLSearchParams(window.location.search);
const room = filterXSS(qs.get('room'));
@@ -150,6 +152,10 @@
.then(function (response) {
console.log(response);
// Store in session
window.sessionStorage.peer_username = username;
window.sessionStorage.peer_password = password;
if (room) {
return (window.location.href =
'/join/' + window.location.search);