# Dokumentasi Teknis: Mekanisme Session Takeover (SSO vs Email/Password) & Real-Time Background Session Checker

Dokumen ini berisi rangkuman teknis, analisis akar masalah, serta solusi yang telah diterapkan pada aplikasi RISE CRM (CodeIgniter 4) di workspace `w:\san_ibb_2jul`.

---

## 1. Analisis & Penyelesaian Sesi Takeover (SSO vs Email/Password)

### 📌 Latar Belakang Masalah
Saat pengguna mencoba login menggunakan **Email & Kata Sandi** ketika akun tersebut sedang aktif di perangkat/browser lain via **SSO (Single Sign-On)**, sistem mengalami perulangan (*redirect loop*) dan selalu mengembalikan notifikasi kuning:
> *"Your account is being used on another device/browser. If this is not you, change your password immediately."*

### 🔍 Akar Masalah (*Root Cause*)
1. Setiap halaman terlindungi (seperti `dashboard/view`, `projects`, `tasks`) memuat `Security_Controller.php`.
2. `Security_Controller` mengecek kecocokan antara `session_id()` browser saat ini dengan kolom `last_session_id` pada tabel `users` di database.
3. Sebelumnya, method `Users_model::authenticate()` hanya memvalidasi kata sandi **tanpa meng-update `last_session_id`** di database.
4. Akibatnya, saat login Email/Password berhasil dan di-*redirect* ke `/dashboard/view`, `Security_Controller` mendapati `session_id` browser tidak cocok dengan DB (`last_session_id` milik SSO lama), sehingga pengguna **seketika ditendang kembali** ke `signin?m=...`.

### 🛠️ Solusi Perbaikan
Pada file `w:\san_ibb_2jul\app\Models\Users_model.php`, saat kata sandi berhasil diverifikasi, sistem langsung memperbarui `last_session_id` dan `is_active` di database:

```php
$current_session_id = session_id();
$this->db_builder->where('id', $user_info->id)->update([
    'last_session_id' => $current_session_id,
    'is_active'       => 1
]);
```

Dengan perubahan ini, login Email/Password secara resmi mengambil alih (*takeover*) sesi aktif dari SSO tanpa tertahan loop.

---

## 2. Arsitektur Background Periodic Jobs (Server-Side Cron)

Sistem RISE CRM memiliki mekanisme tugas periodik otomatis di sisi server (*Server-Side Cron Job*).

### ⚙️ Endpoint Eksekusi
* **URL Trigger**: `https://charly.mayagrahakencana.com/cron` (atau via Linux Crontab `wget -q -O- .../cron`).
* **Controller**: `App\Controllers\Cron.php`
* **Library Utama**: `App\Libraries\Cron_job.php`

### 📋 Daftar Tugas Periodik:

#### A. Tugas Per Jam (Hourly Jobs)
* ✉️ **Email Piping (IMAP / Outlook IMAP)**: Membaca email masuk dan otomatis mengonversinya menjadi Tiket Support.
* 📅 **Sinkronisasi Google Calendar**: Menarik & menyinkronkan event kalender Google.
* 🔄 **Tagihan & Beban Berulang**:
  - `create_recurring_invoices()`: Faktur tagihan rutin.
  - `create_recurring_expenses()`: Pencatatan pengeluaran berulang.
  - `create_subscription_invoices()`: Tagihan berbasis langganan.
  - `create_recurring_tasks()`: Tugas proyek berulang.
  - `create_recurring_reminders()`: Pengingat kalender berulang.
* 🔔 **Notifikasi Email**:
  - Pengingat sebelum & sesudah jatuh tempo faktur.
  - Pengingat *deadline* tugas proyek.
* 🎫 **Auto-Close Tiket**: Menutup tiket customer service yang inaktif.

#### B. Tugas Harian (Daily Jobs)
* 🧹 **Pembersihan Session (`remove_old_session_data`)**: Membersihkan data sesi lama pada tabel `ci_sessions` (> 7 hari).
* 📈 **Workflow Lead (`run_lead_status_transition_workflow`)**: Menjalankan alur otomatisasi & pengingat *follow-up* prospek sales.

---

## 3. Real-Time Background Session Checker (Browser 15-Second Heartbeat)

Untuk memastikan perangkat lama yang sedang diam (*idle*) di dalam CRM langsung ter-kick out secara *real-time* ketika akun di-login dari perangkat lain, dibuatlah mekanisme **15-Second Session Checker**.

### 🛠️ Komponen Yang Diterapkan

#### A. Endpoint Server (`app/Controllers/Signin.php`)
Menambahkan method `check_active_session()`:
```php
function check_active_session()
{
    $login_user_id = $this->Users_model->login_user_id();
    if (!$login_user_id) {
        return $this->response->setJSON(['signed_out' => true]);
    }

    $user_info = $this->Users_model->get_access_info($login_user_id);
    if ($user_info && $user_info->last_session_id) {
        $current_session_id = session_id();
        $stored_session_id = $user_info->last_session_id;

        if (!$user_info->is_admin && !$user_info->is_multi_session && !$user_info->is_debuger) {
            if ($current_session_id !== $stored_session_id) {
                $this->Users_model->setForceLogin(true);
                $this->Users_model->sign_out();
                return $this->response->setJSON(['signed_out' => true]);
            }
        }
    }

    return $this->response->setJSON(['signed_out' => false]);
}
```

#### B. Skrip Background Browser (`app/Views/includes/custom_body_bottom.php`)
Menambahkan heartbeat interval per 15 detik di footer CRM beserta logging Console:

```html
<script type="text/javascript">
    $(document).ready(function () {
        console.log("[SESSION CHECKER] Initializing 15-second active session check loop...");
        
        function runSessionCheck() {
            console.log("[SESSION CHECKER] Heartbeat sent at " + new Date().toLocaleTimeString());
            $.ajax({
                url: "<?php echo get_uri('signin/check_active_session'); ?>",
                dataType: "json",
                cache: false,
                success: function (response) {
                    if (response && response.signed_out) {
                        console.warn("[SESSION CHECKER] Sesi telah diambil alih di perangkat lain. Redirecting...");
                        window.location.href = "<?php echo get_uri('signin?m=Your+account+is+being+used+on+another+device%2Fbrowser.+If+this+is+not+you%2C+change+your+password+immediately.'); ?>";
                    }
                }
            });
        }

        // Jalankan per 15 detik
        setInterval(runSessionCheck, 15000);
    });
</script>
```

---

## 4. Daftar File Yang Dimodifikasi

1. **`app/Models/Users_model.php`**: Updating `last_session_id` & `is_active` saat verifikasi password berhasil.
2. **`app/Controllers/Signin.php`**: Penambahan endpoint AJAX `check_active_session()`.
3. **`app/Views/includes/custom_body_bottom.php`**: Penambahan skrip *Real-Time Session Checker (15 Detik)* & perbaikan sintaksis JS.

---

## 5. Cara Verifikasi & Pengujian

1. Login ke CRM di **Perangkat A** dan buka Console Browser (`F12`).
2. Amati log console:
   - `[SESSION CHECKER] Initializing 15-second active session check loop...`
   - `[SESSION CHECKER] Heartbeat sent at 15:10:00` (berulang per 15 detik).
3. Lakukan login dengan akun yang sama di **Perangkat B**.
4. Dalam waktu **maksimal 15 detik**, **Perangkat A** otomatis ter-kick out dan di-redirect ke halaman signin dengan pesan peringatan kuning.
