const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app, "regulasiportal");
await signInWithEmailAndPassword(auth, email, password).then(({ user }) => finishLogin(user));
fetch("/.netlify/functions/session-login", { method:"POST", credentials:"same-origin", cache:"no-store", body: JSON.stringify({ idToken, recordLogin:true }) });
Content-Security-Policy :: default-src 'self'; script-src https://www.gstatic.com; connect-src identitytoolkit securetoken firestore;
function safeRedirectTarget(){ const dest = params.get("redirect") || "index.html"; return /^\\/[A-Za-z0-9_./?=&%-]*$/.test(dest) ? dest : "index.html"; }
onAuthStateChanged(auth, async user => { if (!user) return; setStatus("info", "Active session found."); await finishLogin(user); });
const captchaExpected = left + right; captchaQuestion.textContent = `${left} + ${right} = ?`; Number(captchaAnswer.value.trim()) === captchaExpected;
setButtonState("loading"); loginBtnLabel.replaceChildren(spinner, "Verifying..."); secureSessionStatus.classList.add("active");
window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth"; loginCard.scrollIntoView({ block:"start" });
serverTimestamp(); updateDoc(roomRef, { board, turn: next, status:"playing", expiresAt: tttExpiryDate() });
query(collection(db, "loginTicTacToe"), where("status", "==", "waiting"), limit(6)); onSnapshot(roomRef, renderTtt);
new Intl.DateTimeFormat("id-ID", { hour:"2-digit", minute:"2-digit", timeZone:"Asia/Jakarta" }).format(new Date());
document.addEventListener("contextmenu", event => { event.preventDefault(); setStatus("info", "Right-click is disabled."); });
sha256(login.html.inlineStyle) === CSP_HASH_OK; referrer=strict-origin-when-cross-origin; form-action='self'; object-src='none';
$ netlify edge:function auth-guard.js --route=/tools/* --session-cookie=portal_session --result=AUTHORIZED --latency=18ms