diff --git a/dashcaddy-api/routes/auth/sso-gate.js b/dashcaddy-api/routes/auth/sso-gate.js
index ef5e64c..61a3888 100644
--- a/dashcaddy-api/routes/auth/sso-gate.js
+++ b/dashcaddy-api/routes/auth/sso-gate.js
@@ -255,34 +255,40 @@ function buildLoginPage(service) {
title: 'Signing in...', bg: '#0a0a0a', accent: '#60a5fa',
body: `if(ls.getItem('token')){go('/?direct=1');return}
d.textContent='Fetching token from DashCaddy...';
-ft('chat').then(function(r){d.textContent+=' Status: '+r.status;return r.text()}).then(function(t){
- d.textContent+='\\n'+t.substring(0,300);
- try{var j=JSON.parse(t);if(j.token){ls.setItem('token',j.token);go('/?direct=1')}
- else{fail('Auto-login unavailable. Sign in at DashCaddy','No token field in response')}}
- catch(e){fail('Auto-login error. Sign in at DashCaddy','Parse error: '+e.message)}
-}).catch(function(e){fail('Could not reach DashCaddy. Sign in at DashCaddy','Fetch error: '+e.message)})`
+ft('chat').then(function(r){return r.text()}).then(function(t){
+ try{var j=JSON.parse(t);if(j.token){ls.setItem('token',j.token);go('/?direct=1');return}
+ // No token but chat is reachable — fall through to manual UI link below
+ fail('Auto-login unavailable. Open Chat manually','No token field: '+t.substring(0,200))}
+ catch(e){fail('Auto-login parse error. Open Chat manually','Error: '+e.message+' / body: '+t.substring(0,200))}
+}).catch(function(e){fail('Could not reach DashCaddy. Open Chat manually','Fetch error: '+(e&&e.message||'unknown'))})`
},
plex: {
title: 'Signing in to Plex...', bg: '#1f1f1f', accent: '#e5a00d',
body: `if(ls.getItem('myPlexAccessToken')){go('/web/?direct=1');return}
ft('plex').then(function(r){return r.json()}).then(function(j){
- if(j.token){ls.setItem('myPlexAccessToken',j.token);d.textContent='Token stored, redirecting...';go('/web/?direct=1')}
- else{fail('Auto-login unavailable. Open Plex manually or re-authenticate at DashCaddy',JSON.stringify(j))}
-}).catch(function(e){fail('Could not reach DashCaddy. Sign in at DashCaddy','Error: '+e.message)})`
+ if(j.token){ls.setItem('myPlexAccessToken',j.token);d.textContent='Token stored, redirecting...';go('/web/?direct=1');return}
+ // No token returned. Three fallbacks in priority order:
+ // 1. Stale token in localStorage — Plex may still accept it.
+ if(ls.getItem('myPlexAccessToken')){go('/web/?direct=1');return}
+ // 2. Manual link so the user is never trapped on this page.
+ fail('Auto-login unavailable. Open Plex manually or re-authenticate at DashCaddy','API: '+JSON.stringify(j))}
+}).catch(function(e){fail('Could not reach DashCaddy. Open Plex manually','Error: '+(e&&e.message||'unknown'))})`
},
jellyfin: {
title: 'Signing in to Jellyfin...', bg: '#101014', accent: '#00a4dc',
body: `ft('jellyfin').then(function(r){return r.json()}).then(function(j){
- if(j.token){merge('jellyfin_credentials',j,'Jellyfin');merge('_jellyfin_credentials',j,'Jellyfin');d.textContent='Token stored, redirecting...';go('/web/')}
- else{fail('Auto-login unavailable. Open Jellyfin manually or re-authenticate at DashCaddy',JSON.stringify(j))}
-}).catch(function(e){fail('Could not reach DashCaddy. Sign in at DashCaddy','Error: '+e.message)})`
+ if(j.token){merge('jellyfin_credentials',j,'Jellyfin');merge('_jellyfin_credentials',j,'Jellyfin');d.textContent='Token stored, redirecting...';go('/web/');return}
+ if(ls.getItem('jellyfin_credentials')||ls.getItem('_jellyfin_credentials')){go('/web/');return}
+ fail('Auto-login unavailable. Open Jellyfin manually or re-authenticate at DashCaddy','API: '+JSON.stringify(j))}
+}).catch(function(e){fail('Could not reach DashCaddy. Open Jellyfin manually','Error: '+(e&&e.message||'unknown'))})`
},
emby: {
title: 'Signing in to Emby...', bg: '#101014', accent: '#52b54b',
body: `ft('emby').then(function(r){return r.json()}).then(function(j){
- if(j.token){merge('emby_credentials',j,'Emby');merge('_emby_credentials',j,'Emby');d.textContent='Token stored, redirecting...';go('/web/')}
- else{fail('Auto-login unavailable. Open Emby manually or re-authenticate at DashCaddy',JSON.stringify(j))}
-}).catch(function(e){fail('Could not reach DashCaddy. Sign in at DashCaddy','Error: '+e.message)})`
+ if(j.token){merge('emby_credentials',j,'Emby');merge('_emby_credentials',j,'Emby');d.textContent='Token stored, redirecting...';go('/web/');return}
+ if(ls.getItem('emby_credentials')||ls.getItem('_emby_credentials')){go('/web/');return}
+ fail('Auto-login unavailable. Open Emby manually or re-authenticate at DashCaddy','API: '+JSON.stringify(j))}
+}).catch(function(e){fail('Could not reach DashCaddy. Open Emby manually','Error: '+(e&&e.message||'unknown'))})`
},
};