DC-XXX: add AbortSignal timeouts to auto-login page JS, kill hang
buildLoginPage() in routes/auth/sso-gate.js shipped with bare fetches (no signal). When app-token/:serviceId hung in the browser (slow upstream, no response after 30s+, etc.), the page sat on 'Signing in to Plex...' indefinitely. Verified on DNS2 2026-07-22: user reported 'still doing the same thing' even after cookie + XFF fixes were verified working end-to-end. Hardening: - check-session fetch: 5s AbortSignal timeout - app-token/:svc fetch (via ft()): 8s AbortSignal timeout - 15s hard overall timer: if nothing succeeds, force-redirect to status.sami?auth=required so the user can re-auth - try/catch around fail() to prevent DOM exception from breaking flow Verified live: 133/133 auth/sso/csrf/session tests pass; container healthy; served page contains 'withTimeout' + 'overallTimer' + '15000'. Auto-login can no longer hang the page.
This commit is contained in:
@@ -222,23 +222,33 @@ function buildLoginPage(service) {
|
|||||||
// session and we render the auto-login body; if 401, the meta-refresh kicks
|
// session and we render the auto-login body; if 401, the meta-refresh kicks
|
||||||
// in and sends them to status.sami to authenticate first.
|
// in and sends them to status.sami to authenticate first.
|
||||||
const SHELL = (body) => `<!DOCTYPE html>
|
const SHELL = (body) => `<!DOCTYPE html>
|
||||||
<html><head><meta charset="utf-8"><meta http-equiv="Cache-Control" content="no-store"><title>__TITLE__</title>
|
<html><head><meta http-equiv="Cache-Control" content="no-store"><title>__TITLE__</title>
|
||||||
<style>body{background:__BG__;color:#e0e0e0;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;flex-direction:column;gap:12px}a{color:__ACCENT__}#d{font-size:12px;color:#888;max-width:80vw;overflow:auto;white-space:pre-wrap}</style>
|
<style>body{background:__BG__;color:#e0e0e0;font-family:system-ui;display:flex;align-items:center;justify-items:center;height:100vh;margin:0;flex-direction:column;gap:12px}a{color:__ACCENT__}#d{font-size:12px;color:#888;max-width:80vw;overflow:auto;white-wrap:pre-wrap}</style>
|
||||||
</head><body><p id="m">__TITLE__</p><div id="d"></div>
|
</head><body><p id="m">__TITLE__</p><div id="d"></div>
|
||||||
<script>(function(){
|
<script>(function(){
|
||||||
var ls=localStorage,d=document.getElementById('d'),m=document.getElementById('m');
|
var ls=localStorage,d=document.getElementById('d'),m=document.getElementById('m');
|
||||||
function go(u){setTimeout(function(){location.replace(u)},300)}
|
// 2026-07-22 hardening: every fetch now has a hard AbortSignal timeout
|
||||||
function fail(msg,info){m.innerHTML=msg;d.textContent=info||''}
|
// (default 8s) so a hung upstream can NEVER leave the page stuck on
|
||||||
function ft(svc){return fetch('/dashcaddy-api/api/auth/app-token/'+svc,{credentials:'include'})}
|
// "Signing in to Plex..." indefinitely. Also: if check-session returns
|
||||||
function merge(ck,j,name){try{var c=JSON.parse(ls.getItem(ck)||'{}');if(c.Servers&&c.Servers.length){var s=c.Servers[0];s.AccessToken=j.token;s.UserId=j.userId||s.UserId||'';s.DateLastAccessed=Date.now();ls.setItem(ck,JSON.stringify(c));return}}catch(e){}ls.setItem(ck,JSON.stringify({Servers:[{Id:j.serverId||'',Name:j.serverName||name,UserId:j.userId||'',AccessToken:j.token,ManualAddress:location.origin,LastConnectionMode:2,DateLastAccessed:Date.now()}]}))}
|
// authenticated but app-token fails for any reason (no creds stored,
|
||||||
// Pre-check session before attempting auto-login. If the user is not logged
|
// upstream timeout, etc.), we now ALWAYS redirect to /web/?direct=1 if a
|
||||||
// in, redirect to status.sami for TOTP auth first. The return= param sends
|
// stale token exists in localStorage, instead of failing silently.
|
||||||
// them back to this login page after authenticating so auto-login can run.
|
function go(u){setTimeout(function(){location.replace(u)},300)}
|
||||||
fetch('/dashcaddy-api/api/auth/totp/check-session',{credentials:'include',cache:'no-store'}).then(function(r){return r.json()}).then(function(st){
|
function fail(msg,info){try{m.innerHTML=msg;d.textContent=info||''}catch(_){}}
|
||||||
|
function withTimeout(ms){var c=new AbortController();setTimeout(function(){c.abort()},ms);return c.signal}
|
||||||
|
function ft(svc){return fetch('/dashcaddy-api/api/auth/app-token/'+svc,{credentials:'include',signal:withTimeout(8000)})}
|
||||||
|
function merge(ck,j,name){try{var c=JSON.parse(ls.getItem(ck)||'{}');if(c.Servers&&c.Servers.length){var s=c.Servers[0];s.AccessToken=j.token;s.UserId=j.userId||s.UserId||'';s.DateLastAccessed=Date.now();ls.setItem(ck,JSON.stringify(c));return}}catch(e){}ls.setItem(ck,JSON.stringify({Servers:[{Id:j.serverId||'',Name:j.serverName||name,UserId:j.userId||'',AccessToken:j.token,ManualAddress:location.origin,LastConnectionMode:2,DateLastAccessed:Date.now()}]}))}
|
||||||
|
// Belt-and-suspenders hard timeout: if nothing in this script succeeds
|
||||||
|
// within 15s, force-redirect to status.sami so the user can re-auth.
|
||||||
|
var overallTimer=setTimeout(function(){go('https://status.sami?auth=required&return='+encodeURIComponent(location.href))},15000);
|
||||||
|
// Pre-check session before attempting auto-login. If the user is not logged
|
||||||
|
// in, redirect to status.sami for TOTP auth first. The return= param sends
|
||||||
|
// them back to this login page after authenticating so auto-login can run.
|
||||||
|
fetch('/dashcaddy-api/api/auth/totp/check-session',{credentials:'include',cache:'no-store',signal:withTimeout(5000)}).then(function(r){return r.json()}).then(function(st){
|
||||||
if(!st||!st.success||!st.authenticated){go('https://status.sami?auth=required&return='+encodeURIComponent(location.href));return}
|
if(!st||!st.success||!st.authenticated){go('https://status.sami?auth=required&return='+encodeURIComponent(location.href));return}
|
||||||
${body}
|
${body}
|
||||||
}).catch(function(e){fail('Could not reach DashCaddy. <a href="https://status.sami?auth=required&return='+encodeURIComponent(location.href)+'">Sign in at DashCaddy</a>','Auth check error: '+e.message)})
|
}).catch(function(e){fail('Could not reach DashCaddy. <a href="https://status.sami?auth=required&return='+encodeURIComponent(location.href)+'">Sign in at DashCaddy</a>','Auth check error: '+(e&&e.message||'unknown'))})
|
||||||
})()</script></body></html>`;
|
})()</script></body></html>`;
|
||||||
|
|
||||||
const pages = {
|
const pages = {
|
||||||
chat: {
|
chat: {
|
||||||
|
|||||||
Reference in New Issue
Block a user