52 lines
2.1 KiB
JavaScript
52 lines
2.1 KiB
JavaScript
// ===== ENCRYPTED VAULT -> SERVICE SSO HANDOFF =====
|
|
(function() {
|
|
function isAllowedReturnUrl(returnUrl, expectedServiceId) {
|
|
if (!returnUrl || !expectedServiceId || !/^[a-z0-9][a-z0-9-]*$/.test(expectedServiceId)) return false;
|
|
try {
|
|
const parsed = new URL(returnUrl, window.location.origin);
|
|
const suffix = SITE.tld.startsWith('.') ? SITE.tld : `.${SITE.tld}`;
|
|
const expectedHost = `${expectedServiceId}${suffix}`;
|
|
return parsed.protocol === 'https:' && parsed.hostname === expectedHost;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function buildHandoffTarget(returnUrl, token, expectedServiceId) {
|
|
if (!isAllowedReturnUrl(returnUrl, expectedServiceId)) return null;
|
|
const parsed = new URL(returnUrl, window.location.origin);
|
|
if (!token) return null;
|
|
|
|
const returnPath = `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
|
// The shared (dashcaddy_auth) Caddy snippet installs this public landing
|
|
// route on every protected host. It rewrites to /api/v1/auth/sso-exchange.
|
|
parsed.pathname = '/dashcaddy-sso';
|
|
parsed.search = '';
|
|
parsed.hash = '';
|
|
parsed.searchParams.set('token', token);
|
|
parsed.searchParams.set('return', returnPath);
|
|
return parsed.toString();
|
|
}
|
|
|
|
async function resume(returnUrl, expectedServiceId, runtime = {}) {
|
|
if (!isAllowedReturnUrl(returnUrl, expectedServiceId)) return false;
|
|
const fetchFn = runtime.fetch || window.fetch.bind(window);
|
|
const locationObj = runtime.location || window.location;
|
|
try {
|
|
const response = await fetchFn(`/api/v1/auth/sso-handoff?serviceId=${encodeURIComponent(expectedServiceId)}`, {
|
|
credentials: 'include',
|
|
cache: 'no-store',
|
|
});
|
|
if (!response.ok) return false;
|
|
const data = await response.json();
|
|
const target = data.success && buildHandoffTarget(returnUrl, data.ssoToken, expectedServiceId);
|
|
if (!target) return false;
|
|
locationObj.replace(target);
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
window.DCCredentialVault = { isAllowedReturnUrl, buildHandoffTarget, resume };
|
|
})(); |