Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions public/classroom_poc.html
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,19 @@ <h2 class="text-sm font-semibold flex items-center gap-2">
const desksContainer = document.getElementById('desksContainer');
const toastContainer = document.getElementById('toastContainer');

// Authentication utils
function getAuth() {
return {
token: localStorage.getItem('edu_token'),
user: JSON.parse(localStorage.getItem('edu_user') || 'null')
};
}

function isAuthenticated() {
const { token, user } = getAuth();
return token && user;
}
Comment thread
Shubhashish-Chakraborty marked this conversation as resolved.

// COORDINATE HELPERS
// Positions are stored and transmitted as NORMALIZED values (0.0 – 1.0)
// representing a fraction of the canvas width/height.
Expand Down Expand Up @@ -414,6 +427,15 @@ <h2 class="text-sm font-semibold flex items-center gap-2">

// Join / Leave
function joinClassroom() {
if (!isAuthenticated()) {
showToast('Please sign in to join the virtual classroom.', 'warning');
// Preserve the user's intended destination so login.html can send
// them back here after authentication.
const returnTo = window.location.pathname + window.location.search;
sessionStorage.setItem('post_login_redirect', returnTo);
setTimeout(() => { window.location.href = '/login.html'; }, 800);
return;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
intentionalDisconnect = false;
reconnectAttempts = 0;
isSeated = false;
Expand Down
11 changes: 8 additions & 3 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,14 @@ <h2 class="text-2xl font-bold mb-2">Global Virtual Classroom</h2>
</div>
<span>21 learners inside</span>
</div>
<a href="/classroom_poc.html?autojoin=1&room=lobby" class="w-full bg-white text-teal-700 font-bold px-6 py-3 rounded-xl hover:bg-teal-50 transition-all shadow-lg flex items-center justify-center gap-2">
<i class="fas fa-door-open"></i> Join Lobby Classroom
</a>
<div id="classroom-join-container">
<a href="/classroom_poc.html?autojoin=1&room=lobby" class="w-full bg-white text-teal-700 font-bold px-6 py-3 rounded-xl hover:bg-teal-50 transition-all shadow-lg flex items-center justify-center gap-2" id="join-logged-in" style="display: none;">
<i class="fas fa-door-open"></i> Join Lobby Classroom
</a>
<a href="/login.html?next=%2Fclassroom_poc.html%3Fautojoin%3D1%26room%3Dlobby" class="w-full bg-white/20 text-white font-bold px-6 py-3 rounded-xl hover:bg-white hover:text-black transition-all shadow-lg flex items-center justify-center gap-2" id="join-not-logged-in" style="display: none;">
<i class="fas fa-sign-in-alt"></i> Login to Join Classroom
</a>
</div>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</div>
</div>
</div>
Expand Down
12 changes: 12 additions & 0 deletions public/js/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,16 @@ document.addEventListener('DOMContentLoaded', async () => {
await inject('site-navbar', '/partials/navbar.html', updateAuthSection);
await inject('site-footer', '/partials/footer.html');
updateDarkModeIcon();
});

// Conditional rendering for the "Join Lobby Classroom" button.
// These elements only exist on the homepage, so we guard against nulls.
document.addEventListener('DOMContentLoaded', () => {
const loggedInEl = document.getElementById('join-logged-in');
const notLoggedInEl = document.getElementById('join-not-logged-in');
if (!loggedInEl || !notLoggedInEl) return;

const authed = isAuthenticated();
loggedInEl.style.display = authed ? 'flex' : 'none';
notLoggedInEl.style.display = authed ? 'none' : 'flex';
});
42 changes: 40 additions & 2 deletions public/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,33 @@ <h2 class="text-xl font-bold text-gray-800 dark:text-gray-200 mb-2">Create your
: 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300');
}

// Store authentication data
// Store authentication data and handle post-login flow
function storeAuth(data) {
localStorage.setItem('edu_token', data.token);
localStorage.setItem('edu_user', JSON.stringify(data.user));
window.location.href = '/dashboard.html';

// Show success toast
showToast('Redirecting...');

// Wait a few seconds then redirect
setTimeout(() => {
handlePostLoginRedirect();
}, 500);
}

// Show success toast message
function showToast(msg) {
const toast = document.createElement('div');
toast.className = 'fixed top-4 right-4 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-lg p-4 text-green-700 dark:text-green-300 text-sm flex items-start gap-2 animate-pulse shadow-lg z-50';
const icon = document.createElement('i');
icon.className = 'fas fa-check-circle mt-0.5 flex-shrink-0';
const span = document.createElement('span');
span.textContent = msg;
toast.append(icon, span);
document.body.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 3000);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Show error message
Expand All @@ -274,12 +296,28 @@ <h2 class="text-xl font-bold text-gray-800 dark:text-gray-200 mb-2">Create your
}, 5000);
}

// Handle redirect after successful login
function handlePostLoginRedirect() {
const redirectUrl = sessionStorage.getItem('post_login_redirect');
sessionStorage.removeItem('post_login_redirect');
// Only honor same-origin, path-only redirects to prevent open-redirect abuse.
const safe = redirectUrl && /^\/(?!\/)/.test(redirectUrl);
window.location.href = safe ? redirectUrl : '/dashboard.html';
}
Comment on lines +299 to +306
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Open-redirect risk via post_login_redirect.

handlePostLoginRedirect() navigates to whatever URL sits in sessionStorage['post_login_redirect'] without validating that it's same-origin. Combined with the ?next= handler at lines 321‑323, an attacker can craft https://yoursite/login.html?next=https%3A%2F%2Fevil.example%2Fphish — after a legitimate login, the user is silently bounced to evil.example, which is a classic phishing vector.

Recommend constraining to same-origin relative paths only:

🛡️ Suggested guard
 function handlePostLoginRedirect() {
-    // Check if there's a stored redirect URL
     const redirectUrl = sessionStorage.getItem('post_login_redirect');
-    
-    if (redirectUrl) {
-        // Clear it so it doesn't trigger again
-        sessionStorage.removeItem('post_login_redirect');
-        // Redirect to the original destination
-        window.location.href = redirectUrl;
-    } else {
-        // Default redirect
-        window.location.href = '/dashboard.html';
-    }
+    sessionStorage.removeItem('post_login_redirect');
+    // Only honor same-origin, path-only redirects to prevent open-redirect abuse.
+    const safe = redirectUrl && /^\/(?!\/)/.test(redirectUrl);
+    window.location.href = safe ? redirectUrl : '/dashboard.html';
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@public/login.html` around lines 298 - 312, handlePostLoginRedirect currently
redirects to whatever is in sessionStorage['post_login_redirect'] allowing
open-redirects; change it to validate the stored value before navigating: when
reading redirectUrl (from sessionStorage.getItem('post_login_redirect')),
construct a URL object with location.origin as the base (new URL(redirectUrl,
location.origin)) and only allow the redirect if url.origin === location.origin
and url.protocol is http: or https: and url.pathname starts with '/'; also
explicitly reject values starting with '//' or 'javascript:' and fallback to the
default '/dashboard.html' if validation fails, then remove the stored item and
perform the safe navigation in handlePostLoginRedirect.


// Handle tab parameter in URL
const params = new URLSearchParams(location.search);
if (params.get('tab') === 'register') {
switchTab('register');
}

// Handle next parameter for post-login redirect.
// URLSearchParams.get() already performs percent-decoding — no need to decode again.
const nextParam = params.get('next');
if (nextParam) {
sessionStorage.setItem('post_login_redirect', nextParam);
}

// Login form submission
document.getElementById('form-login').addEventListener('submit', async e => {
e.preventDefault();
Expand Down
Loading