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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user