🔐 Muzibu Session & Auth Sistemi

Tam Dokümantasyon: Login, Session, Device Limit, Redis, Database
📅 2025-12-10 🎯 Tenant: muzibu.com

1. Genel Bakış (Basit Anlatım)

🎯 Ne İşe Yarıyor?

Session sistemi = Kullanıcının kim olduğunu takip etme

Device limit = Aynı anda kaç cihazda müzik dinleyebileceğini kontrol etme

LIFO (Last In First Out) = Yeni giriş yapan, eski girişi atar

🔄 Basit Akış

Kullanıcı Login Redis'e Session DB'ye Kayıt Cookie'ye ID Müzik Dinle!

2. Veriler Nereye Kaydediliyor?

🔴 Redis

  • ✅ Session verisi (kullanıcı bilgisi)
  • ✅ CSRF token
  • ✅ Flash messages
  • ✅ Geçici cache

TTL: 10080 dakika (7 gün)
Key format: 40 karakter alphanumeric

🔵 Database

  • user_active_sessions tablosu
  • ✅ Cihaz bilgisi (browser, platform)
  • ✅ IP adresi
  • ✅ Son aktivite zamanı

Tablo: tenant_muzibu_1528d0.user_active_sessions
Kullanım: Device limit kontrolü

🟢 Cookie

  • ✅ Session ID (40 karakter)
  • ✅ XSRF-TOKEN
  • ✅ HttpOnly flag
  • ✅ Secure flag

Cookie adı: tuufi_session
Şifreleme: Laravel encrypt

3. Redis Ne Yapıyor?

📦 Redis'te Saklanan

Key: AKmX6ilTCPbot7IukdA8H3NblqBNmXp4qfJfcPKN

Value: {
  "_token": "csrf_token_here",
  "_previous": {"url": "https://muzibu.com"},
  "login_web_...": 1,  // User ID
  "password_hash_web_...": "hash"
}

✅ Redis'in Görevi

  • Hız: Database'den 100x hızlı
  • Session storage: Laravel session driver
  • TTL: Otomatik expire (7 gün)
  • Atomik: Race condition yok

⚠️ Redis Session Silinirse Ne Olur?

Kullanıcı otomatik logout olur. Sayfa yenileyince login sayfasına yönlendirilir. Ama user_active_sessions tablosundaki kayıt kalır (stale data). Bu nedenle 60 dakika sonra temizlenir.

4. Database Ne Yapıyor?

📊 user_active_sessions Tablosu

Kolon Tip Açıklama
id bigint Primary key
user_id bigint Kullanıcı ID (FK → users)
session_id varchar(255) Redis session ID (unique)
ip_address varchar(45) Kullanıcı IP
device_type varchar(20) desktop / mobile / tablet
device_name varchar(100) OS X - Chrome
last_activity timestamp Son aktivite (30 sn'de güncellenir)

🎯 Tablo Ne İşe Yarıyor?

  • Device limit kontrolü: Kaç cihaz aktif?
  • LIFO uygulaması: Yeni giriş, eskiyi siler
  • Aktif cihaz listesi: Kullanıcıya göster
  • Cihaz sonlandırma: Seçili cihazı çıkar

⚠️ DB'den Session Silinirse Ne Olur?

Frontend her 30 saniyede /api/auth/check-session çağırıyor. Session DB'de yoksa → "session_terminated" döner → Kullanıcı logout olur.

Ama Redis'te session hala var! Bu durumda Laravel "giriş yapmış" sanır, ama DeviceService "session yok" der. Sonuç: Çelişki ve logout.

5. Login Akışı

1

Kullanıcı Form'u Gönderir

Email + Password → POST /api/auth/login

2

Laravel Auth::attempt()

Şifre doğrulanır, session regenerate edilir, yeni session ID oluşur

$request->session()->regenerate();
3

Redis'e Session Kaydedilir

Session driver redis olduğu için otomatik yazılır

Redis KEY: AKmX6ilTCPbot7IukdA8...
TTL: 10080 dakika (7 gün)
4

DeviceService.registerSession()

Session ID + cihaz bilgisi → user_active_sessions tablosuna

DB::table('user_active_sessions')->insert([
    'user_id' => $user->id,
    'session_id' => session()->getId(),
    'device_name' => 'OS X - Chrome',
    ...
]);
5

LIFO: Eski Sessionlar Silinir

Device limit aşıldıysa en eski session silinir

// enforceDeviceLimit()
// Limit: 1 → 2. cihaz giriş yapınca 1. cihaz silinir
// Limit: 2 → 3. cihaz giriş yapınca 1. cihaz silinir
6

Cookie Gönderilir

Session ID şifrelenerek cookie'ye yazılır

Set-Cookie: tuufi_session=eyJpdiI6...; HttpOnly; Secure; SameSite=Lax

6. Logout Akışı

✅ Normal Logout

  1. Kullanıcı "Çıkış" butonuna tıklar
  2. POST /api/auth/logout çağrılır
  3. DeviceService.unregisterSession() → DB'den siler
  4. Auth::logout() → Laravel logout
  5. session()->invalidate() → Redis'ten siler
  6. Cookie expire edilir
  7. Login sayfasına yönlendirilir

⚡ Zorla Logout (LIFO)

  1. Başka cihazdan login yapılır
  2. enforceDeviceLimit() çalışır
  3. Eski session DB'den silinir
  4. Frontend 30 sn'de check-session çağırır
  5. API "session_terminated" döner
  6. Frontend logout yapar + login'e yönlendirir

⚠️ Redis session hala var ama DB'de yok → Çelişki

7. Cihaz Limiti (LIFO)

📊 Limit Hiyerarşisi (3-Tier)

1. User Override

users.device_limit

2. Subscription Plan

plans.device_limit

3. Tenant Setting

auth_device_limit

// DeviceService::getDeviceLimit()
1. User override: $user->device_limit (null değilse)
2. Subscription Plan: $user->subscriptions->plan->device_limit
3. Tenant setting: setting('auth_device_limit') → Default: 1

🔄 LIFO Nasıl Çalışıyor?

Zaman İşlem Sonuç
09:00 Bilgisayardan login Session A aktif
10:00 Telefondan login Session B aktif, Session A siliniyor...
10:00:30 Bilgisayar check-session Session A yok → Logout!
10:01 Bilgisayar login sayfasına "Başka cihazdan giriş yapıldı"

8. Kullanıcıyı Nasıl Çıkarırız?

✅ Yöntem 1: DB'den Sil

-- Belirli kullanıcının tüm sessionlarını sil
DELETE FROM user_active_sessions
WHERE user_id = 1;

-- Sonuç: 30 saniye içinde logout olur

Frontend polling "session yok" alır → Logout

✅ Yöntem 2: Redis'ten Sil

# Session ID'yi bul
SELECT session_id FROM user_active_sessions
WHERE user_id = 1;

# Redis'ten sil
redis-cli DEL "AKmX6ilTCPbot7IukdA8..."

# Sonuç: ANINDA logout

Laravel session yok → Anında unauthorized

✅ Yöntem 3: API ile

// Admin panelden
DeviceService::terminateSession($sessionId);

// Veya API endpoint (ileride)
POST /api/admin/users/{id}/logout

✅ Yöntem 4: Tinker ile

php artisan tinker
>>> tenancy()->initialize(\App\Models\Tenant::find(1001));
>>> DB::table('user_active_sessions')
...     ->where('user_id', 1)
...     ->delete();

⚠️ Dikkat!

Sadece DB'den silmek yetmez! Redis'te session hala var olabilir.
Kullanıcı logout olduktan sonra bile cookie ile request atabilir.
En güvenli: Hem DB hem Redis'ten sil.

9. Şarkı Dinleme Akışı

🎵 Stream İsteği Geldiğinde

1

GET /api/songs/{id}/stream

2

Auth kontrolü: auth('web')->user()

Giriş yapmamış → 30 sn preview

3

Session DB'de var mı?

DB::table('user_active_sessions')
    ->where('session_id', session()->getId())
    ->exists();
4

Session yoksa → 401 session_terminated

Session varsa → Device limit kontrolü

5

Premium kontrolü: $user->isPremiumOrTrial()

Premium değil → 30 sn preview

6

✅ Signed URL döndür → Şarkı çalar

🔐 Signed URL Nedir?

MP3/HLS dosyasına erişim için geçici şifrelenmiş URL.
30 dakika sonra expire olur, tekrar stream isteği gerekir.
Bu sayede direkt link paylaşılsa bile çalışmaz.

10. Mevcut Sorunlar & Çözümler

🐛 Sorun 1: "Premium'a Geçin" Açılışta Gösteriliyor

Neden?

localStorage'dan önceki state yükleniyor.
Eğer preview mode'da kaldıysa, isPreviewBlocked = true oluyor.
Play butonuna basınca "Premium'a geçin" toast çıkıyor.

Çözüm

State restore sırasında isPreviewBlocked flag'ini sıfırla.
Veya login olduktan sonra flag'i temizle.

🐛 Sorun 2: Otomatik Şarkı Çalıyor

Neden?

loadState() fonksiyonu önceki queue'yu yüklüyor.
Sonra playSongFromQueue() çağrılıyor (autoplay=false bile olsa).
Stream API çağrısı yapılıyor, URL alınıyor.

Çözüm

State restore sırasında sadece UI güncellemesi yap.
Şarkıyı yükleme, stream isteği atma.
Kullanıcı play'e basınca yükle.

⚠️ Sorun 3: Redis-DB Senkronizasyon

Neden?

Redis session 7 gün yaşar.
DB session 60 dakika inaktifse silinir.
İkisi arasında senkronizasyon yok.

Çözüm

DB cleanup TTL'ini Redis ile eşitle.
Veya logout sırasında her ikisini de sil.