*/ public function for(string $guard, int $id, ?string $currentUuid = null): Collection { return DB::table('login_sessions as ls') ->join('user_devices as d', 'd.id', '=', 'ls.device_id') ->join('sessions as s', 's.id', '=', 'ls.session_id') ->where('d.guard', $guard) ->where('d.authenticatable_id', $id) ->where('s.last_activity', '>=', now()->subMinutes((int) config('session.lifetime'))->getTimestamp()) ->orderByDesc('ls.last_seen_at') ->get([ 'ls.uuid as uuid', 'ls.ip_address as ip_address', 'ls.last_seen_at as last_seen_at', 'd.name as device', 'd.uuid as device_uuid', ]) ->map(function (object $row) use ($currentUuid) { $row->is_current = $currentUuid !== null && $row->uuid === $currentUuid; $row->last_seen_at = $row->last_seen_at ? \Illuminate\Support\Carbon::parse($row->last_seen_at) : null; return $row; }); } /** End one session. Returns false when it is not this identity's to end. */ public function end(string $guard, int $id, string $uuid): bool { $row = DB::table('login_sessions as ls') ->join('user_devices as d', 'd.id', '=', 'ls.device_id') ->where('d.guard', $guard) ->where('d.authenticatable_id', $id) ->where('ls.uuid', $uuid) ->first(['ls.id as id', 'ls.session_id as session_id']); if ($row === null) { return false; } $this->forget($row->session_id); DB::table('login_sessions')->where('id', $row->id)->delete(); return true; } /** * End everything except the session asking. * * The current one is excluded rather than ended and re-created: signing * somebody out of the page they are using to sign other people out is not * what the button says, and it leaves them unable to see whether it worked. * * @return int how many were ended */ public function endOthers(string $guard, int $id, ?string $currentUuid): int { $rows = DB::table('login_sessions as ls') ->join('user_devices as d', 'd.id', '=', 'ls.device_id') ->where('d.guard', $guard) ->where('d.authenticatable_id', $id) ->when($currentUuid !== null, fn ($q) => $q->where('ls.uuid', '!=', $currentUuid)) ->get(['ls.id as id', 'ls.session_id as session_id']); foreach ($rows as $row) { $this->forget($row->session_id); } DB::table('login_sessions')->whereIn('id', $rows->pluck('id'))->delete(); return $rows->count(); } /** The uuid of the session making this request, if it has one. */ public function currentUuid(): ?string { $uuid = session()->get(LoginSession::KEY); return is_string($uuid) && $uuid !== '' ? $uuid : null; } /** * Delete the framework's own row, which is what actually signs the browser * out: its next request finds no session and starts an empty one. */ private function forget(?string $sessionId): void { if ($sessionId !== null && $sessionId !== '') { DB::table('sessions')->where('id', $sessionId)->delete(); } } }