Bind app sessions to the platform auth session.
Deploy Ladill Hosting / deploy (push) Successful in 27s

Clear local sessions when auth.ladill.com signs out and ping the platform session from keepalive.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-20 08:26:30 +00:00
co-authored by Cursor
parent ae1c93b017
commit e6c2ebeee0
5 changed files with 94 additions and 3 deletions
@@ -138,6 +138,18 @@ class SsoLoginController extends Controller
return redirect()->away($this->defaultSignedOutUrl());
}
/** Platform session ended — clear this app and offer silent sign-in again. */
public function platformSignedOut(Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('sso.connect', [
'redirect' => (string) $request->query('redirect', ''),
]);
}
public function logoutBridge(Request $request): View
{
$return = $this->safeReturnUrl((string) $request->query('return', ''));
@@ -0,0 +1,58 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Symfony\Component\HttpFoundation\Response;
/**
* App sessions are subordinate to the platform (auth.ladill.com) session.
* When the platform session ends, clear this app's local session too.
*/
class EnsurePlatformSession
{
public function handle(Request $request, Closure $next): Response
{
if (! Auth::check()) {
return $next($request);
}
$authDomain = trim((string) config('app.auth_domain', ''));
if ($authDomain === '') {
return $next($request);
}
$cookieHeader = (string) $request->headers->get('Cookie', '');
if ($cookieHeader === '') {
return $this->clearAppSession($request);
}
try {
$response = Http::withHeaders(['Cookie' => $cookieHeader])
->timeout(3)
->get('https://'.$authDomain.'/sso/ping');
} catch (\Throwable) {
return $next($request);
}
if ($response->status() === 401) {
return $this->clearAppSession($request);
}
return $next($request);
}
private function clearAppSession(Request $request): Response
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('sso.connect', [
'redirect' => $request->fullUrl(),
]);
}
}
+3
View File
@@ -19,6 +19,9 @@ return Application::configure(basePath: dirname(__DIR__))
$middleware->web(append: [
\App\Http\Middleware\SetActingAccount::class,
]);
$middleware->alias([
'platform.session' => \App\Http\Middleware\EnsurePlatformSession::class,
]);
// External registrar webhook posts have no CSRF token.
$middleware->validateCsrfTokens(except: [
@@ -1,4 +1,21 @@
@if (auth()->check())
{{-- Same-site iframe keeps the shared auth.ladill.com session warm while using this app. --}}
<iframe src="https://{{ config('app.auth_domain') }}/sso/ping" hidden width="0" height="0" title=""></iframe>
@php
$authPing = 'https://'.config('app.auth_domain').'/sso/ping';
$platformSignedOutUrl = route('sso.platform-signed-out', ['redirect' => url()->current()]);
@endphp
<script>
(function () {
const pingUrl = @js($authPing);
const signedOutUrl = @js($platformSignedOutUrl);
const ping = () => fetch(pingUrl, { credentials: 'include' })
.then((response) => {
if (response.status === 401) {
window.location.href = signedOutUrl;
}
})
.catch(() => {});
ping();
setInterval(ping, 5 * 60 * 1000);
})();
</script>
@endif
+2 -1
View File
@@ -28,9 +28,10 @@ Route::get('/sso/callback', [SsoLoginController::class, 'callback'])->name('sso.
Route::post('/logout', [SsoLoginController::class, 'logout'])->name('logout');
Route::get('/sso/logout-bridge', [SsoLoginController::class, 'logoutBridge'])->name('sso.logout-bridge');
Route::get('/sso/logout-frontchannel', [SsoLoginController::class, 'frontchannelLogout'])->name('sso.logout-frontchannel');
Route::get('/sso/platform-signed-out', [SsoLoginController::class, 'platformSignedOut'])->name('sso.platform-signed-out');
Route::get('/signed-out', fn () => auth()->check() ? redirect()->route('hosting.dashboard') : view('hosting.signed-out'))->name('hosting.signed-out');
Route::middleware(['auth'])->group(function () use ($serversApp) {
Route::middleware(['auth', 'platform.session'])->group(function () use ($serversApp) {
Route::get('/notifications', [NotificationController::class, 'index'])->name('notifications.index');
Route::get('/notifications/unread', [NotificationController::class, 'unread'])->name('notifications.unread');
Route::post('/notifications/{id}/read', [NotificationController::class, 'markAsRead'])->name('notifications.mark-read');