🐛

Login Sonrası Session Terminate Loop

Muzibu - Kritik Bug Tespit Raporu

KRİTİK Race Condition Cookie Timing Sonsuz Döngü

🚨 Kullanıcı Tarafından Görülen Semptomlar

  1. Kullanıcı giriş yapıyor
  2. Anasayfaya yönlendiriliyor ✓
  3. Hemen ardından "Oturum Sonlandırıldı" modalı çıkıyor
  4. Modal "Tamam" butonuna basınca login sayfasına gidiyor
  5. Login sayfası otomatik anasayfaya yönlendiriyor (kullanıcı authenticated)
  6. Anasayfada yine session terminated modal çıkıyor
  7. Sonsuz döngü! Kullanıcı müzik dinleyemiyor

📝 Basit Anlatım (Sorun Nedir?)

🎯 Kök Neden: Cookie Zamanlama Sorunu

Giriş yaptığınızda sistem size bir kimlik kartı (cookie) vermesi gerekiyor. Ama bu kimlik kartı browser'ınıza ulaşmadan önce, sistem "kimliğiniz var mı?" diye soruyor.

Cookie henüz gelmediği için sistem "hayır yok" diyor ve sizi çıkış yapıyor.

🔄 Sonsuz Döngü Nasıl Oluşuyor?

  1. Giriş yap → Cookie henüz gelmedi
  2. 2 saniye sonra kontrol et → Cookie yok → Çıkış yap
  3. Login sayfasına yönlendir
  4. Login sayfası: "Kullanıcı zaten giriş yapmış" → Anasayfaya yönlendir
  5. Anasayfada tekrar kontrol → Yine cookie yok → Tekrar çıkış
  6. Sonsuz döngü!

⏱️ Zamanlama Problemi

0ms: Login başarılı → Cookie queue'ya eklendi
50ms: Anasayfaya redirect
100ms: Anasayfa yükleniyor
200ms: Player.js yüklendi
2200ms: Session kontrolü → Cookie HENÜZ YOK!
2201ms: Logout!

🔧 Teknik Detaylar (Geliştiriciler İçin)

1️⃣ Race Condition: Cookie vs Session Check

AuthenticatedSessionController.php:store() (satır 149-168)

Satır 151: $request->session()->regenerate() - Session ID yenileniyor
Satır 168: $deviceService->registerSession($user) - Session DB'ye kaydediliyor
Satır 206-212: redirect($intendedUrl) - Anasayfaya yönlendiriliyor

DeviceService.php:registerSession() (satır 146)

cookie()->queue(cookie('mzb_login_token', ...))

Cookie queue'ya ekleniyor (hemen set edilmiyor!)

session.js:startSessionPolling() (satır 29-33)

setTimeout(() => this.checkSessionValidity(), 2000)

2 saniye sonra session kontrolü yapılıyor

❌ Cookie henüz browser'a ulaşmamış olabilir!

2️⃣ Session Check Mantığı - İkili Kontrol Sorunu

AuthController.php:checkSession() (satır 194-207)

✅ FALLBACK VAR:
Cookie yoksa → Tek session varsa → Cookie re-issue et → Geçerli say
Bu kod çalışıyor ve doğru

AuthController.php:checkSession() (satır 212)

❌ AMA SONRA:
if (!$deviceService->sessionExists($user))
Bu method TEKRAR cookie kontrolü yapıyor!

DeviceService.php:sessionExists() (satır 280)

$cookieToken = request()->cookie('mzb_login_token')
Cookie yoksa → FALSE dönüyor
Fallback var (satır 289-325) ama...
Cookie yoksa tek session varsa re-issue ediyor (DOĞRU)
Ama yine de bir sonraki check'te cookie olmayabilir

3️⃣ Sonsuz Döngü Mekanizması

session.js:handleSessionTerminated() (satır 196)

window.location.href = '/login?session_terminated=1'

Hard redirect ile login sayfasına gidiyor

AuthenticatedSessionController.php:create() (satır 24-29)

if (Auth::check()) return redirect('/')
❌ SORUN BURDA!
Kullanıcı hala authenticated (logout tam olmadı)
Login page otomatik anasayfaya yönlendiriyor
Anasayfada tekrar session check → Cookie yok → Logout → Loop!

4️⃣ Cookie Queue Mechanism

Laravel Cookie Middleware:

1. Controller: cookie()->queue(...) - Cookie queue'ya eklenir
2. Response oluşturulur (redirect)
3. Middleware: Queue'daki cookie'ler response'a Set-Cookie header olarak eklenir
4. Browser: Response gelince cookie'yi kaydeder
5. Redirect takip edilir (yeni request)
6. Yeni request'te cookie gönderilir

⏱️ Bu süreç: ~50-200ms (network + browser processing)

⚠️ Ama session check 2000ms sonra başlıyor ve her 5 saniyede bir devam ediyor!

💡 İlk check'te cookie YENİDEN set edilebilir (fallback var), ama ikinci check'te cookie varsa geçerli, yoksa LIFO kick sanılıyor!

📊 Bug Akış Diyagramı

0ms
POST /login → Başarılı ✓
10ms
registerSession() → DB'ye kayıt ✓
15ms
cookie()->queue() → Cookie queue'ya eklendi ⏳
20ms
redirect('/') → Response dönüyor
50ms
Browser: Set-Cookie header alındı → Cookie kaydediliyor
100ms
GET / → Anasayfa yükleniyor
200ms
Player.js yüklendi → startSessionPolling()
250ms
Cookie browser'da SET ✓ (teoride)
2.2s
checkSessionValidity() çağrıldı
2.21s
sessionExists() → Cookie kontrolü → FALLBACK (re-issue)
7.2s
2. checkSessionValidity() (5 saniye sonra)
7.21s
sessionExists() → Cookie YOK/PROBLEM → FALSE → LOGOUT!
7.25s
handleSessionTerminated() → Modal + Hard redirect
7.3s
GET /login?session_terminated=1
7.35s
Auth::check() = TRUE → redirect('/')
7.4s
GET / → Anasayfa tekrar yükleniyor
9.4s
checkSessionValidity() → YİNE FALSE → LOOP!
♾️ SONSUZ DÖNGÜ

🎯 Kök Neden Analizi

Ana Sorun #1: Cookie Re-Issue Yeterli Değil

sessionExists() içinde cookie yoksa re-issue ediliyor (satır 299). Ama bu cookie response ile browser'a gitmesi lazım. Eğer aynı request içinde birden fazla kez sessionExists() çağrılırsa, cookie henüz set olmamış olabilir.

→ Cookie queue'da ama browser'da değil. O request'te hala "cookie yok" dönüyor.

Ana Sorun #2: İkinci Check'te Fallback Çalışmıyor

İlk session check (2.saniye): Cookie yok → Fallback çalışıyor → Cookie re-issue → TRUE dönüyor ✓

İkinci session check (7.saniye): Cookie hala yok/sorunu var → Fallback kodu "bir session var" kontrolü yapıyor. Ama bu sefer multiple session olabilir (aynı user birden fazla cihaz), o yüzden fallback FALSE dönüyor.

→ Kod: "Tek session değil, LIFO tarafından kick edilmiş olmalı" diyor ve logout yapıyor.

Ana Sorun #3: Auth::check() vs Session DB

handleSessionTerminated() logout yapıyor ve login'e yönlendiriyor.

Ama Auth::logout() server-side session'ı hemen silmiyor olabilir (timing). Login page'de Auth::check() hala TRUE dönüyor.

→ Login page otomatik anasayfaya redirect → Tekrar check → Loop!

💡 Çözüm Önerileri (Öncelik Sırasıyla)

🥇

Çözüm #1: Login Sonrası Grace Period (En Hızlı)

Login'den sonraki ilk 10-15 saniye boyunca session check'lerde cookie yoksa toleranslı ol.

Dosya: AuthController.php:checkSession()

// Login timestamp'i session'a kaydet
// Login sonrası 15 saniye içindeyse ve cookie yoksa:
if (time() - session('login_timestamp') < 15) {
    // Tek session varsa geçerli say, logout yapma
    return ['valid' => true];
}
🥈

Çözüm #2: Session Check Delay Artır

İlk session check'i 2 saniye değil 5 saniye sonra yap. Cookie kesinlikle set olmuş olur.

Dosya: session.js:29

setTimeout(() => {
    this.checkSessionValidity();
}, 5000); // 2000 → 5000

⚠️ Ama bu kesin çözüm değil. Yavaş network'te gene sorun olabilir.

🥉

Çözüm #3: Session DB ID Kullan (Cookie Yerine)

Cookie kontrolü yerine session ID ile DB kontrol et. Session ID Laravel session'dan gelir, cookie'ye ihtiyaç yok.

Dosya: DeviceService.php:sessionExists()

// Cookie yerine session ID kullan
$sessionId = $this->getCurrentSessionId();
$exists = DB::table($this->table)
    ->where('user_id', $user->id)
    ->where('session_id', $sessionId)
    ->exists();

⚠️ Ama bu LIFO mantığını bozabilir (aynı browser'dan logout/login'de yeni cihaz algılanır).

🔧

Çözüm #4: Login Page Auth Check Düzelt

Login page'de authenticated kullanıcıyı hemen redirect etme. Önce session terminate nedeni kontrol et.

Dosya: AuthenticatedSessionController.php:create() satır 24

if (Auth::check()) {
    // Session terminated parametresi varsa logout yap
    if (request()->get('session_terminated')) {
        Auth::logout();
        // Login formunu göster
    } else {
        return redirect('/');
    }
}
⚙️

Çözüm #5: Session Terminated Flag (Sonsuz Loop Önleme)

handleSessionTerminated() çağrıldığında bir flag set et. Bu flag varken session check yapma.

Dosya: session.js

// Flag ekle
sessionTerminatedHandled: false,

handleSessionTerminated() {
    this.sessionTerminatedHandled = true;
    localStorage.setItem('session_terminated_at', Date.now());
    // ... logout işlemleri
}

checkSessionValidity() {
    // Loop önleme
    const terminatedAt = localStorage.getItem('session_terminated_at');
    if (terminatedAt && Date.now() - terminatedAt < 30000) {
        return; // Son 30 saniyede terminated olduysa check yapma
    }
    // ... normal check
}

🎯 Önerilen Çözüm Kombinasyonu (En İyi Yaklaşım)

Aşağıdaki 3 çözümü birlikte uygula:

1.
Login Grace Period ekle (Çözüm #1)

→ Login sonrası 15 saniye tolerans

2.
Login Page Auth Check düzelt (Çözüm #4)

→ session_terminated parametresi varsa logout yap

3.
Session Terminated Flag ekle (Çözüm #5)

→ Loop önleme mekanizması

✅ Bu kombinasyon:

  • Cookie timing sorununu çözer (grace period)
  • Sonsuz loop'u önler (flag + login page check)
  • Mevcut LIFO mantığını bozmaz
  • Minimal kod değişikliği gerektirir