[grade=B] fix(auth): reuse valid session for cross-host SSO
This commit is contained in:
+42
-2
@@ -267,6 +267,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
function buildSsoHandoffTarget(returnUrl, token) {
|
||||
const parsed = new URL(returnUrl, window.location.origin);
|
||||
if (parsed.origin === window.location.origin) return parsed.toString();
|
||||
|
||||
const suffix = SITE.tld.startsWith('.') ? SITE.tld : `.${SITE.tld}`;
|
||||
const isPrivateHost = parsed.hostname === suffix.slice(1) || parsed.hostname.endsWith(suffix);
|
||||
if (parsed.protocol !== 'https:' || !isPrivateHost || !token) return null;
|
||||
|
||||
const returnPath = `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
||||
parsed.pathname = '/dashcaddy-sso';
|
||||
parsed.search = '';
|
||||
parsed.hash = '';
|
||||
parsed.searchParams.set('token', token);
|
||||
parsed.searchParams.set('return', returnPath);
|
||||
return parsed.toString();
|
||||
}
|
||||
|
||||
async function resumeExistingSession(returnUrl) {
|
||||
if (!returnUrl || !isAllowedReturnUrl(returnUrl)) return false;
|
||||
try {
|
||||
const res = await fetch('/api/v1/auth/sso-handoff', {
|
||||
credentials: 'include',
|
||||
cache: 'no-store',
|
||||
});
|
||||
if (!res.ok) return false;
|
||||
const data = await res.json();
|
||||
const target = data.success && buildSsoHandoffTarget(returnUrl, data.ssoToken);
|
||||
if (!target) return false;
|
||||
try { sessionStorage.removeItem('totp_redirect'); } catch (_) {}
|
||||
window.location.replace(target);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.get('auth') === 'required') {
|
||||
// Preserve the gated service destination so submitTotpCode() can append
|
||||
@@ -277,8 +313,12 @@
|
||||
}
|
||||
// Clean URL — happens after we've captured the redirect
|
||||
window.history.replaceState({}, '', window.location.pathname);
|
||||
// Show on next tick so the DOM (the .totp-card) is ready
|
||||
setTimeout(show, 0);
|
||||
// Reuse the valid status.sami session first. Only show the TOTP/provider
|
||||
// challenge when that session is genuinely absent or expired.
|
||||
setTimeout(async () => {
|
||||
if (await resumeExistingSession(returnUrl)) return;
|
||||
await show();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Expose for hot-trigger from other modules (e.g. logout)
|
||||
|
||||
Reference in New Issue
Block a user