diff --git a/app/Config/Auth.php b/app/Config/Auth.php index dcdad94..d90f0df 100644 --- a/app/Config/Auth.php +++ b/app/Config/Auth.php @@ -443,22 +443,88 @@ class Auth extends ShieldAuth $session = session(); $beforeLoginUrl = (string) ($session->getTempdata('beforeLoginUrl') ?? ''); - // Some temporary/internal URLs can be stored as beforeLoginUrl in production - // and then hijack post-login landing. Skip those and use configured default. + // Always consume and clear beforeLoginUrl so it is never reused across logins + if ($beforeLoginUrl !== '') { + $session->removeTempdata('beforeLoginUrl'); + } + + // Validate beforeLoginUrl: strictly prevent redirecting to API, AJAX, polling, + // background workers, static assets, or non-HTML endpoints that hijack post-login landing. if ($beforeLoginUrl !== '') { $path = (string) parse_url($beforeLoginUrl, PHP_URL_PATH); $path = '/' . ltrim($path, '/'); - $blockedPaths = [ - '/doctor_work/document_workspace', - '/opd/appointment', + $pathLower = strtolower($path); + + $isInvalidLanding = false; + + // 1. Root, login, or logout endpoints + if ($pathLower === '' || $pathLower === '/' || str_starts_with($pathLower, '/login') || str_starts_with($pathLower, '/logout')) { + $isInvalidLanding = true; + } + + // 2. Action keywords associated with AJAX, JSON, or background requests + $invalidKeywords = [ + '/fetch', + '/ajax', + '/api', + '/poll', + '/status', + '/sync', + '/token', + '/search', + '/lookup', + '/callback', + '/webhook', + '/check', + '/captcha', + '/verify', ]; - if (in_array(strtolower($path), $blockedPaths, true)) { + foreach ($invalidKeywords as $kw) { + if (str_contains($pathLower, $kw)) { + $isInvalidLanding = true; + break; + } + } + + // 3. Controllers or routes that perform background processing or modular sub-views + if (! $isInvalidLanding) { + $blockedPrefixes = [ + '/abdmopdqueue', + '/abdmgateway', + '/abdmhiu', + '/abdmsync', + '/abdmcodingpanel', + '/abdmfhirinspector', + '/abdmbridgelog', + '/auth/', + '/doctor_work/document_workspace', + '/opd/appointment', + ]; + foreach ($blockedPrefixes as $prefix) { + if (str_starts_with($pathLower, $prefix)) { + $isInvalidLanding = true; + break; + } + } + } + + // 4. File extensions (assets, documents, JSON) + if (! $isInvalidLanding && preg_match('/\.(json|xml|csv|pdf|png|jpg|jpeg|gif|ico|css|js|woff|woff2|ttf|map)$/i', $pathLower)) { + $isInvalidLanding = true; + } + + if ($isInvalidLanding) { $beforeLoginUrl = ''; - $session->removeTempdata('beforeLoginUrl'); } } - $url = $beforeLoginUrl !== '' ? $beforeLoginUrl : (string) setting('Auth.redirects')['login']; + try { + $defaultLogin = (string) (setting('Auth.redirects')['login'] ?? '/'); + } catch (\Throwable $e) { + $defaultLogin = (string) ($this->redirects['login'] ?? '/'); + } + + $url = $beforeLoginUrl !== '' ? $beforeLoginUrl : $defaultLogin; return $this->getUrl($url); } diff --git a/app/Filters/LoginThrottleFilter.php b/app/Filters/LoginThrottleFilter.php index c572d15..8bc96fe 100644 --- a/app/Filters/LoginThrottleFilter.php +++ b/app/Filters/LoginThrottleFilter.php @@ -51,6 +51,27 @@ class LoginThrottleFilter implements FilterInterface return; } + // Cleanse any stale or invalid beforeLoginUrl (e.g. from background AJAX polling) + $session = session(); + $staleUrl = (string) ($session->getTempdata('beforeLoginUrl') ?? ''); + if ($staleUrl !== '') { + $stalePath = strtolower((string) parse_url($staleUrl, PHP_URL_PATH)); + if ( + str_contains($stalePath, '/fetch') + || str_contains($stalePath, '/ajax') + || str_contains($stalePath, '/api') + || str_contains($stalePath, 'abdmopdqueue') + || str_contains($stalePath, 'abdmgateway') + || str_contains($stalePath, 'abdmhiu') + || str_contains($stalePath, 'abdmsync') + || str_contains($stalePath, 'auth/') + || str_contains($stalePath, 'captcha') + || str_contains($stalePath, 'doctor_work') + ) { + $session->removeTempdata('beforeLoginUrl'); + } + } + // Only throttle state-changing requests (POST) if (strtoupper($request->getMethod()) !== 'POST') { return; diff --git a/app/Views/abdm/opd_queue.php b/app/Views/abdm/opd_queue.php index a1025e1..64d1bf3 100644 --- a/app/Views/abdm/opd_queue.php +++ b/app/Views/abdm/opd_queue.php @@ -391,17 +391,33 @@ } function loadQueue() { - const date = document.getElementById('queueDate').value; - const status = document.getElementById('queueStatus').value; + const bodyEl = document.getElementById('abdmQueueBody'); + const dateEl = document.getElementById('queueDate'); + if (!bodyEl || !dateEl) { + clearTimeout(autoTimer); + return; + } + + const date = dateEl.value; + const status = document.getElementById('queueStatus')?.value || ''; const url = BASE + 'AbdmOpdQueue/fetch?date=' + encodeURIComponent(date) + '&status=' + encodeURIComponent(status); - fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) - .then(r => r.json()) + fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } }) + .then(r => { + if (r.status === 401 || r.status === 403) { + clearTimeout(autoTimer); + throw new Error('Session unauthenticated. Polling stopped.'); + } + return r.json(); + }) .then(renderQueue) .catch(err => { - document.getElementById('abdmQueueBody').innerHTML = - `