/* Login page logic for the Work Package Suite. One view. Sign in posts to /api/auth/login, the server authenticates by binding to the domain over LDAPS (D13), and on success sets an HttpOnly session cookie — not readable from here, which is the point — after which we redirect to ?next= or the home page. The password entered is the person's WINDOWS password. There is no forgot-password flow and no reset view: the suite holds no password to reset. "Forgot password?" is a plain external link to Okta in login.html, so there is deliberately no click handler for it here — one that called preventDefault() would swallow the navigation. */ (function () { 'use strict'; var errorBox = document.getElementById('error'); var okBox = document.getElementById('ok'); function byId(id) { return document.getElementById(id); } function showError(msg) { okBox.classList.remove('show'); errorBox.textContent = msg; errorBox.classList.add('show'); } function clearBanners() { errorBox.classList.remove('show'); okBox.classList.remove('show'); } // Where to go after signing in: the ?next= param if it's a safe same-site // path, otherwise the home page. (Reject absolute/scheme URLs to avoid an // open-redirect.) function nextTarget() { try { var next = new URLSearchParams(location.search).get('next') || ''; if (next && next.charAt(0) === '/' && next.charAt(1) !== '/') return next; } catch (e) {} return 'index.html'; } function postJson(url, payload) { return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }).then(function (r) { return r.json().catch(function () { return null; }).then(function (j) { return { status: r.status, ok: r.ok, json: j }; }); }); } function detail(res, fallback) { var d = res && res.json && res.json.detail; return (typeof d === 'string' && d) ? d : fallback; } // ── sign in ──────────────────────────────────────────────────────────────── var form = byId('login-form'); var submitBtn = byId('submit'); // Guarded: an unguarded addEventListener on null would break sign-in itself if a // cached older login.html were served. if (!form || !submitBtn) return; form.addEventListener('submit', function (e) { e.preventDefault(); clearBanners(); var username = byId('username').value.trim(); var password = byId('password').value; if (!username || !password) { showError('Enter your username and password.'); return; } submitBtn.disabled = true; submitBtn.textContent = 'Signing in…'; postJson('/api/auth/login', { username: username, password: password }) .then(function (res) { if (res.ok) { location.replace(nextTarget()); return; } if (res.status === 401) showError('Invalid username or password.'); else if (res.status === 403) showError(detail(res, 'Your account is disabled.')); else if (res.status === 429) showError(detail(res, 'Too many failed attempts. Try again later.')); // 503 means the directory is unreachable or misconfigured — OUR fault, not a // wrong password. Saying so stops people hunting for a password they no // longer have while a deploy is broken. else if (res.status === 503) showError(detail(res, 'Sign-in is temporarily unavailable. Contact IT.')); else showError(detail(res, 'Sign-in failed (HTTP ' + res.status + ').')); submitBtn.disabled = false; submitBtn.textContent = 'Sign in'; }) .catch(function () { showError('Could not reach the server. Check your connection and try again.'); submitBtn.disabled = false; submitBtn.textContent = 'Sign in'; }); }); })();