feat: Add password-based authentication for broadcasters and restrict WebRTC offers to authenticated users.
This commit is contained in:
@@ -4,6 +4,8 @@ const startBtn = document.getElementById('startBtn');
|
|||||||
const localVideo = document.getElementById('localVideo');
|
const localVideo = document.getElementById('localVideo');
|
||||||
const statusDot = document.getElementById('statusDot');
|
const statusDot = document.getElementById('statusDot');
|
||||||
const statusText = document.getElementById('statusText');
|
const statusText = document.getElementById('statusText');
|
||||||
|
const passwordInput = document.getElementById('broadcasterPassword');
|
||||||
|
const authContainer = document.getElementById('authContainer');
|
||||||
|
|
||||||
let activeStream;
|
let activeStream;
|
||||||
|
|
||||||
@@ -18,6 +20,11 @@ const config = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
socket.on('authError', (msg) => {
|
||||||
|
alert(msg);
|
||||||
|
stopSharing();
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('viewer', id => {
|
socket.on('viewer', id => {
|
||||||
if (!activeStream) return;
|
if (!activeStream) return;
|
||||||
|
|
||||||
@@ -97,6 +104,12 @@ socket.on('disconnectPeer', id => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
startBtn.addEventListener('click', async () => {
|
startBtn.addEventListener('click', async () => {
|
||||||
|
const password = passwordInput.value;
|
||||||
|
if (!password) {
|
||||||
|
alert("Please enter the stream password to broadcast.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stream = await navigator.mediaDevices.getDisplayMedia({
|
const stream = await navigator.mediaDevices.getDisplayMedia({
|
||||||
video: {
|
video: {
|
||||||
@@ -112,10 +125,11 @@ startBtn.addEventListener('click', async () => {
|
|||||||
localVideo.classList.add('active');
|
localVideo.classList.add('active');
|
||||||
|
|
||||||
startBtn.style.display = 'none';
|
startBtn.style.display = 'none';
|
||||||
|
authContainer.style.display = 'none';
|
||||||
statusDot.classList.add('active');
|
statusDot.classList.add('active');
|
||||||
statusText.innerText = 'Sharing Screen (1080p60)';
|
statusText.innerText = 'Sharing Screen (1080p60)';
|
||||||
|
|
||||||
socket.emit('broadcaster');
|
socket.emit('broadcaster', password);
|
||||||
|
|
||||||
stream.getVideoTracks()[0].onended = () => {
|
stream.getVideoTracks()[0].onended = () => {
|
||||||
stopSharing();
|
stopSharing();
|
||||||
@@ -130,6 +144,7 @@ startBtn.addEventListener('click', async () => {
|
|||||||
|
|
||||||
function stopSharing() {
|
function stopSharing() {
|
||||||
startBtn.style.display = 'inline-block';
|
startBtn.style.display = 'inline-block';
|
||||||
|
authContainer.style.display = 'block';
|
||||||
localVideo.classList.remove('active');
|
localVideo.classList.remove('active');
|
||||||
statusDot.classList.remove('active');
|
statusDot.classList.remove('active');
|
||||||
statusText.innerText = 'Not Sharing';
|
statusText.innerText = 'Not Sharing';
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
<div class="container glass">
|
<div class="container glass">
|
||||||
<h1>Share Your Screen</h1>
|
<h1>Share Your Screen</h1>
|
||||||
<p>Broadcast your screen at 1080p 60fps to viewers.</p>
|
<p>Broadcast your screen at 1080p 60fps to viewers.</p>
|
||||||
|
<div id="authContainer" style="margin-bottom: 1.5rem;">
|
||||||
|
<input type="password" id="broadcasterPassword" placeholder="Enter stream password" style="padding: 0.8rem 1rem; border-radius: 8px; border: 1px solid var(--glass-border); background: rgba(0,0,0,0.2); color: white; width: 100%; max-width: 300px; font-family: inherit; font-size: 1rem; outline: none;">
|
||||||
|
</div>
|
||||||
<button id="startBtn" class="btn primary-btn">Start Screenshare</button>
|
<button id="startBtn" class="btn primary-btn">Start Screenshare</button>
|
||||||
<div class="status-indicator">
|
<div class="status-indicator">
|
||||||
<span class="dot" id="statusDot"></span>
|
<span class="dot" id="statusDot"></span>
|
||||||
|
|||||||
12
server.js
12
server.js
@@ -5,13 +5,22 @@ const server = http.createServer(app);
|
|||||||
const { Server } = require("socket.io");
|
const { Server } = require("socket.io");
|
||||||
const io = new Server(server);
|
const io = new Server(server);
|
||||||
|
|
||||||
|
// Password setting via environment variable, defaulting to "secret"
|
||||||
|
const BROADCASTER_PASSWORD = process.env.BROADCASTER_PASSWORD;
|
||||||
|
let broadcasterSocketId = null;
|
||||||
|
|
||||||
app.use(express.static("public"));
|
app.use(express.static("public"));
|
||||||
|
|
||||||
io.on("connection", (socket) => {
|
io.on("connection", (socket) => {
|
||||||
console.log("a user connected:", socket.id);
|
console.log("a user connected:", socket.id);
|
||||||
|
|
||||||
// When the broadcaster starts sharing
|
// When the broadcaster starts sharing
|
||||||
socket.on("broadcaster", () => {
|
socket.on("broadcaster", (password) => {
|
||||||
|
if (password !== BROADCASTER_PASSWORD) {
|
||||||
|
socket.emit("authError", "Invalid broadcaster password.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
broadcasterSocketId = socket.id;
|
||||||
socket.broadcast.emit("broadcaster");
|
socket.broadcast.emit("broadcaster");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -22,6 +31,7 @@ io.on("connection", (socket) => {
|
|||||||
|
|
||||||
// WebRTC Signaling
|
// WebRTC Signaling
|
||||||
socket.on("offer", (id, message) => {
|
socket.on("offer", (id, message) => {
|
||||||
|
if (socket.id !== broadcasterSocketId) return; // Prevent hijacking
|
||||||
socket.to(id).emit("offer", socket.id, message);
|
socket.to(id).emit("offer", socket.id, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user