Device & Session Management — Auth

Device & Session Management adalah fitur yang memungkinkan user melihat dan mengelola semua sesi aktif mereka — dan merevoke akses dari device yang tidak…

Device & session management adalah halaman "Perangkat" di pengaturan akun: daftar semua tempat akunmu sedang login, lengkap dengan perangkat, lokasi perkiraan, dan kapan terakhir dipakai, plus tombol untuk mengeluarkan salah satunya. Bayangin kartu anggota gym yang dicek ke sistem di setiap pintu putar. Kalau kartumu hilang, resepsionis memblokirnya di sistem, dan tap berikutnya langsung ditolak. Bandingkan dengan tiket kertas yang tercetak "berlaku sampai 17.00": tiket itu tetap sah sampai jam itu, siapa pun yang memegangnya.

🧪 Tebakan cepat: pukul 07.20 Siti mengganti password di laptop dan menekan "Keluarkan perangkat lain". HP-nya baru saja dapat access token JWT pukul 07.12, berumur 15 menit, dan server gak menyimpan daftar token yang dicabut. Sampai jam berapa HP itu masih bisa membuka rapor? Tebak dulu, jawabannya ada di bagian "Kenapa pencabutannya langsung berlaku".

Satu baris per perangkat

Siti login di tiga tempat (seperti di lesson Auth Best Practices). Server Portal Sekolah menyimpan satu baris per login:

id Perangkat IP Wilayah Terakhir aktif
sess_1_0 Laptop: Chrome 141, Windows 11 36.68.10.21 Bandung 07.20, perangkat ini
sess_1_1 HP: Chrome 141, Android 15 182.253.5.9 Bandung 07.12
sess_1_2 Komputer lab: Firefox 143, Ubuntu 36.68.200.4 Bandung kemarin 13.40

Kolom tiap baris:

Di Laravel dengan session driver database, tabel sessions sudah punya kolom id, user_id, ip_address, user_agent dan last_activity. Untuk token API, tabel personal_access_tokens milik Sanctum menyimpan hash token dan last_used_at.

Kode: buat, daftar, cabut

function createSessionManager() {
  const sessions = []; // di server sungguhan: tabel sessions

  return {
    create(userId, deviceName, ip) {
      const session = { id: "sess_" + userId + "_" + sessions.length, userId, deviceName, ip, lastActive: Date.now(), revoked: false };
      sessions.push(session);
      return session;
    },
    getSessions(userId) {
      return sessions.filter((s) => s.userId === userId && s.revoked === false);
    },
    revoke(sessionId) {
      const session = sessions.find((s) => s.id === sessionId);
      if (!session) return false;
      session.revoked = true;
      return true;
    },
    revokeAllExcept(userId, keepSessionId) {
      sessions.filter((s) => s.userId === userId && s.id !== keepSessionId).forEach((s) => { s.revoked = true; });
    },
  };
}

const sm = createSessionManager();
sm.create(1, "Chrome 141, Windows 11", "36.68.10.21"); // sess_1_0, laptop
sm.create(1, "Chrome 141, Android 15", "182.253.5.9"); // sess_1_1, HP
sm.create(1, "Firefox 143, Ubuntu", "36.68.200.4");    // sess_1_2, komputer lab
sm.create(2, "Safari 26, iOS 26", "114.124.7.88");     // sess_2_3, Pak Andi

console.log(sm.getSessions(1).length); // 3
console.log(sm.revoke("sess_1_2"));    // true, komputer lab dikeluarkan
console.log(sm.getSessions(1).length); // 2
sm.revokeAllExcept(1, "sess_1_0");     // setelah ganti password
console.log(sm.getSessions(1).map((s) => s.deviceName)); // [ 'Chrome 141, Windows 11' ]
console.log(sm.getSessions(2).length); // 1, sesi Pak Andi gak tersentuh

Kenapa pencabutannya langsung berlaku

Dengan sesi di server, setiap request membawa session ID, dan server mencarinya di store, seperti di lesson Session. Begitu sess_1_2 ditandai revoked, request berikutnya dari komputer lab dijawab 401 {"message": "Unauthenticated."}, detik itu juga.

JWT yang stateless lain cerita (lesson JSON Web Token (JWT)). Server cuma mengecek tanda tangan dan exp, tanpa mencari apa pun. Token HP dibuat pukul 07.12 dengan umur 15 menit, jadi exp-nya 07.27. Mencabut sesi pukul 07.20 menghentikan refresh token-nya, tapi access token yang sudah ada tetap sah. HP Siti masih bisa membuka rapor sampai 07.27. Itu jawaban tebakannya.

Cara menutup celah itu: buat access token sangat pendek dan cabut refresh token-nya (lesson Token Refresh Flow), atau simpan daftar jti yang dicabut di Redis dan periksa di setiap request. Cara kedua artinya server mencari sesuatu lagi di setiap request, persis seperti sesi.

Ganti password: keluarkan perangkat lain

Setelah password diganti, semua sesi lain harus berakhir, kecuali sesi yang sedang dipakai. Kalau password lama sempat bocor, perangkat yang login dengan password itu ikut keluar. Di Laravel:

// Route-nya memakai middleware 'auth.session'
Auth::logoutOtherDevices($request->input('password'));

// Atau langsung, dengan session driver database:
DB::table('sessions')
    ->where('user_id', $request->user()->id)
    ->where('id', '!=', $request->session()->getId())
    ->delete();

Lalu tulis satu baris token_revoke di audit log (lesson Audit Logs & Auth Trail), dan kirim email ke Siti: "Password-mu diganti, dua perangkat lain sudah dikeluarkan."

💡 Login yang mencurigakan

Halaman Perangkat juga tempat yang pas untuk menandai login aneh. Dua pola yang paling berguna:

⚠️ Jebakan yang sering ketemu

TL;DR: Halaman Perangkat menampilkan satu baris per login: id (simpan hash-nya), user_id, perangkat dari User-Agent, IP dan wilayah perkiraan, last_active_at, dan revoked_at. getSessions menyaring milik user yang belum dicabut, revoke menandai satu sesi, revokeAllExcept mencabut semua milik user itu kecuali sesi yang sedang dipakai, dan dipanggil setelah ganti password. Sesi di server dicabut seketika, karena server mencarinya di setiap request. JWT stateless tetap sah sampai exp, kecuali server menyimpan daftar token yang dicabut. Untuk login aneh: impossible travel dan skor risiko dengan ambang (MFA, email, izinkan).

Yang akan kamu pelajari