(async () => {
// -------------------------------
// Funzione: fetch + decrypt via Web Crypto API
// -------------------------------
async function fetchAndDecrypt(fetchUrl, token) {
// 1️⃣ Build the URL and call the endpoint
const url = new URL(fetchUrl);
url.searchParams.set('token', token);
const resp = await fetch(url.toString(), { method: 'GET' });
if (!resp.ok) {
throw new Error(`Fetch failed: ${resp.status}`);
}
// 2️⃣ Parse the JSON envelope
const { token: B64 } = await resp.json();
// map -/_ back to +/
let b64 = B64.replace(/-/g, '+').replace(/_/g, '/');
// pad with '=' to multiple of 4
while (b64.length % 4) b64 += '=';
// decode
const jsonStr = atob(b64);
return JSON.parse(jsonStr);
}
// -------------------------------
// Impostazioni iniziali
// -------------------------------
const usp = new URLSearchParams(window.location.search);
const token = usp.get('token');
const fetchUrl = usp.get('fetch_url');
const memberIdParam = usp.get('member_id');
let nextUrl = usp.get('next');
const resetPasswordUrl = usp.get('reset_password_url') ;
const csu = window.Kajabi?.currentSiteUser?.id;
let email, password, rememberMe, memberId;
try {
// 1️⃣ Se il member_id in GET coincide con l’utente loggato => redirect immediato
// if (memberIdParam && csu != null && String(csu) === memberIdParam) {
// console.log(`Member ID ${memberIdParam} coincide con l'utente; redirect a next.`);
// window.location.href = nextUrl;
// return;
// // 2️⃣ Altrimenti, se ho token+fetchUrl, fetch+decrypt
// } else
if (token && fetchUrl) {
console.log('Token presente; recupero credenziali cifrate.');
const creds = await fetchAndDecrypt(fetchUrl, token);
({ next: nextUrl, email, password, remember_me: rememberMe, member_id: memberId } = creds);
// 3️⃣ Fallback sui GET params
} else {
console.log('Fallback: uso GET params standard.');
nextUrl = usp.get('next');
email = usp.get('email');
password = usp.get('password');
rememberMe= usp.get('remember_me') || usp.get('remember');
memberId = usp.get('member_id');
}
if (!nextUrl) {
nextUrl = '/library';
}
// -------------------------------
// Helpers comuni
// -------------------------------
const utils = {
getCsrfToken() {
const m = document.querySelector('meta[name="csrf-token"]');
return m ? m.getAttribute('content') : null;
},
deleteAllCookies() {
document.cookie.split(';').forEach(c => {
const name = c.split('=')[0].trim();
['', `;domain=${location.hostname}`].forEach(suffix => {
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/` + suffix;
});
});
},
redirect(url) {
window.location.href = url;
},
isOnTargetPage(url) {
try {
const cur = new URL(location.href, location.origin);
const tgt = new URL(url, location.origin);
return cur.origin === tgt.origin
&& cur.pathname === tgt.pathname
&& cur.search === tgt.search;
} catch {
return false;
}
},
async fetchHiddenFields(path) {
const r = await fetch(path);
if (!r.ok) throw new Error(`${path} fetch failed: ${r.status}`);
const doc = new DOMParser().parseFromString(await r.text(), 'text/html');
const utf8 = doc.querySelector('input[name="utf8"]')?.value;
const auth_token = doc.querySelector('input[name="authenticity_token"]')?.value;
if (!utf8 || !auth_token) throw new Error('Hidden fields missing');
return { utf8, auth_token };
}
};
// -------------------------------
// Flow di logout server-side
// -------------------------------
async function serverLogout() {
const csrf = utils.getCsrfToken();
if (!csrf) throw new Error('CSRF token not found');
await fetch('/logout', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
'_method': 'delete',
'authenticity_token': csrf
})
});
console.log('Server-side logout attempt done.');
}
// -------------------------------
// Flow di login
// -------------------------------
// -------------------------------
// Beacon endpoint Brain per client-side errors (incident 2026-05-25).
// Estrae l'origin Brain dal resetPasswordUrl (stesso host).
// -------------------------------
function brainClientErrorUrl() {
try {
const u = new URL(resetPasswordUrl);
return `${u.origin}/sso/__client_error__`;
} catch {
return null;
}
}
async function reportClientError(errorType) {
const url = brainClientErrorUrl();
if (!url) return;
try {
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ slg_token: token || '', error_type: errorType }),
keepalive: true,
});
} catch (err) {
console.error('Brain beacon POST failed:', err);
}
}
function showUserError(message) {
try {
// Sostituisce il loader (se presente) con un alert visibile.
const supportMail = 'assistenza@datamasters.it';
const html = `
Sincronizzazione credenziali fallita
`;
document.body.innerHTML = html;
} catch (err) {
console.error('showUserError failed:', err);
alert(`Sincronizzazione credenziali fallita. ${message}. Contatta assistenza@datamasters.it.`);
}
}
async function performLogin(retry = false) {
const { utf8, auth_token } = await utils.fetchHiddenFields('/login');
const params = new URLSearchParams({ utf8, authenticity_token: auth_token });
if (email) params.append('member[email]', email);
if (password) params.append('member[password]', password);
if (rememberMe) params.append('member[remember_me]', rememberMe);
const resp = await fetch('/login', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params.toString(),
redirect: 'manual'
});
console.log('Login attempt status:', resp.status, 'type:', resp.type);
if (resp.type === 'opaqueredirect') {
console.log('Login succeeded.');
return utils.redirect(nextUrl);
}
// ❌ Login failed
if (!retry) {
console.warn('Login failed. Calling recovery API and retrying once.');
let resetOk = false;
try {
const resetUrl = new URL(resetPasswordUrl); // already absolute
if (token) resetUrl.searchParams.set('token', token);
// const (no implicit global); status check rigoroso -> abort + beacon
// se !=200 (incident 2026-05-25: il vecchio codice loggava e proseguiva
// al retry con LE STESSE credenziali, mascherando i fallimenti reset).
const r = await fetch(resetUrl.toString(), { method: 'GET' });
if (r.status !== 200) {
console.error('Reset API call failed with status:', r.status);
await reportClientError('reset_password_failed');
showUserError('Il sistema non e\' riuscito a risincronizzare la tua password.');
return; // ABORT — no retry con credenziali stale
}
resetOk = true;
} catch (err) {
console.error('Reset API call failed:', err);
await reportClientError('reset_password_failed');
showUserError('Errore di comunicazione con il sistema di sincronizzazione.');
return;
}
if (!resetOk) return;
// Retry login once con credenziali (potenzialmente) appena sincronizzate.
return await performLogin(true);
}
// ❌ Retry also failed: NO redirect anonimo, ABORT + beacon + messaggio
await reportClientError('login_retry_failed');
showUserError('La verifica delle credenziali e\' fallita anche dopo la sincronizzazione.');
return;
}
// -------------------------------
// Verifica accesso a pagina protetta
// -------------------------------
async function probeAndMaybeLogin() {
if (utils.isOnTargetPage(nextUrl)) {
console.log(`Already on ${nextUrl}`); return;
}
const probe = await fetch(nextUrl, { method: 'GET', redirect: 'manual' });
if (probe.type === 'opaqueredirect' || probe.status === 302) {
console.log('Protected, initiating login flow.');
await performLogin();
} else {
console.log('Already authenticated, redirecting.');
utils.redirect(nextUrl);
}
}
// -------------------------------
// Inizio flusso principale
// -------------------------------
if (csu == null) {
console.warn('Nessun currentSiteUser.id; salto controlli.');
return utils.redirect(nextUrl);
}
const actualId = String(csu);
if (actualId === '-1') {
console.log('Utente non loggato; provo login.');
await performLogin();
}
if (memberId && memberId !== actualId) {
console.log('Member ID mismatch; logout + re-login.');
utils.deleteAllCookies();
await serverLogout();
await performLogin();
}
await probeAndMaybeLogin();
} catch (err) {
console.error('Errore nel flusso:', err);
// window.location.href = nextUrl || '/';
}
})();
${message}
Contatta il supporto:
${supportMail}