feat: Add password-based authentication for broadcasters and restrict WebRTC offers to authenticated users.

This commit is contained in:
2026-02-23 03:21:50 +01:00
parent 6ca87857f2
commit e43af71392
3 changed files with 30 additions and 2 deletions

View File

@@ -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';

View File

@@ -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>

View File

@@ -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);
}); });