Improve login security and user experience on admin panel

Add token validation on boot and auto-logout on 401 errors in the admin relay panel.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 1c574898-7c6a-475e-8f63-129c59af48e7
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/67YBlXt
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
alexpaynex
2026-03-19 21:00:19 +00:00
parent ca8cbee179
commit 66eb8ed394

View File

@@ -490,13 +490,19 @@ let refreshTimer = null;
// ── Auth ─────────────────────────────────────────────────────────────────────
async function verifyToken(token) {
const r = await fetch(BASE + '/api/admin/relay/stats', {
headers: { Authorization: 'Bearer ' + token }
}).catch(() => null);
if (!r || r.status === 401) return null;
return r;
}
async function submitToken() {
const val = document.getElementById('token-input').value.trim();
if (!val) return;
const r = await fetch(BASE + '/api/admin/relay/stats', {
headers: { Authorization: 'Bearer ' + val }
});
if (r.status === 401) {
const r = await verifyToken(val);
if (!r) {
document.getElementById('auth-error').style.display = 'block';
return;
}
@@ -530,10 +536,23 @@ async function showMain(initialStats) {
// ── API helpers ──────────────────────────────────────────────────────────────
// api() wraps fetch with Bearer auth. On 401, clears stored token and forces
// back to auth gate so the user is never stuck in a degraded "logged-in" state
// with a stale/expired token.
async function api(path, opts) {
opts = opts || {};
const headers = Object.assign({ Authorization: 'Bearer ' + adminToken, 'Content-Type': 'application/json' }, opts.headers || {});
return fetch(BASE + '/api' + path, Object.assign({}, opts, { headers }));
const r = await fetch(BASE + '/api' + path, Object.assign({}, opts, { headers }));
if (r.status === 401) {
localStorage.removeItem(LS_KEY);
adminToken = '';
clearInterval(refreshTimer);
document.getElementById('main').style.display = 'none';
document.getElementById('auth-gate').style.display = 'block';
document.getElementById('auth-error').style.display = 'block';
document.getElementById('auth-error').textContent = 'Session expired or token changed — please re-authenticate.';
}
return r;
}
// ── Stats ────────────────────────────────────────────────────────────────────
@@ -725,13 +744,21 @@ function toast(msg, type) {
}
// ── Boot ─────────────────────────────────────────────────────────────────────
// Validate any saved token before showing the main panel to avoid a degraded
// "logged-in" state if the token was rotated or never valid.
(function boot() {
(async function boot() {
var saved = localStorage.getItem(LS_KEY);
if (saved) {
adminToken = saved;
showMain(null);
if (!saved) return;
var r = await verifyToken(saved);
if (!r) {
localStorage.removeItem(LS_KEY);
return;
}
adminToken = saved;
var stats = null;
try { stats = await r.json(); } catch(e) {}
showMain(stats);
})();
</script>
</body>