schema([ Section::make('Browser Sessions') ->description('Manage and log out your active sessions on other browsers and devices.') ->schema([ TextEntry::make('session_content') ->hiddenLabel() ->state('If necessary, you may log out of all of your other browser sessions across all of your devices. Some of your recent sessions are listed below; however, this list may not be exhaustive. If you feel your account has been compromised, you should also update your password.'), ...$this->getDynamicSchema(), ]) ->footerActions([ Action::make('deleteBrowserSessions') ->label('Log Out Other Browser Sessions') ->requiresConfirmation() ->modalHeading('Log Out Other Browser Sessions') ->modalDescription('Please enter your password to confirm you would like to log out of your other browser sessions across all of your devices.') ->modalSubmitActionLabel('Log Out Other Browser Sessions') ->form([ TextInput::make('password') ->password() ->revealable() ->label('Password') ->required(), ]) ->action(function (array $data): void { self::logoutOtherBrowserSessions($data['password']); }) ->modalWidth('2xl'), ]), ]); } /** * @return array */ private function getDynamicSchema(): array { $sections = []; foreach ($this->getSessions() as $session) { $sections[] = Section::make() ->schema([ TextEntry::make('device') ->hiddenLabel() ->icon(isset($session->device['desktop']) ? 'heroicon-o-computer-desktop' : 'heroicon-o-device-phone-mobile') ->state(($session->device['platform'] ?? 'Platform').' - '.($session->device['browser'] ?? 'Browser')), TextEntry::make('browser') ->hiddenLabel() ->icon('heroicon-o-map-pin') ->state($session->ip_address), TextEntry::make('time') ->hiddenLabel() ->icon('heroicon-o-clock') ->state($session->last_active), TextEntry::make('is_current_device') ->hiddenLabel() ->visible(fn () => $session->is_current_device) ->state('This device') ->color('primary'), ])->columns(4); } return $sections; } /** * @return array */ private function getSessions(): array { /** @var User $user */ $user = Auth::user(); if (config(key: 'session.driver') !== 'database') { return []; } return collect( value: DB::connection(config(key: 'session.connection'))->table(table: config(key: 'session.table', default: 'sessions')) ->where(column: 'user_id', operator: $user->getAuthIdentifier()) ->latest(column: 'last_activity') ->get() )->map(callback: function ($session): object { $agent = $this->createAgent($session); return (object) [ 'device' => [ 'browser' => $agent->browser(), 'desktop' => $agent->isDesktop(), 'mobile' => $agent->isMobile(), 'tablet' => $agent->isTablet(), 'platform' => $agent->platform(), ], 'ip_address' => $session->ip_address, 'is_current_device' => $session->id === request()->session()->getId(), 'last_active' => 'Last seen '.Carbon::createFromTimestamp($session->last_activity)->diffForHumans(), ]; })->toArray(); } private function createAgent(mixed $session): Agent { return tap( value: new Agent, callback: fn ($agent): string => $agent->setUserAgent(userAgent: $session->user_agent) ); } private function logoutOtherBrowserSessions(string $password): void { /** @var User $user */ $user = Auth::user(); if (! Hash::check($password, $user->password)) { Notification::make() ->danger() ->title('The password you entered was incorrect. Please try again.') ->send(); return; } Auth::guard()->logoutOtherDevices($password); request()->session()->put([ 'password_hash_'.Auth::getDefaultDriver() => $user->getAuthPassword(), ]); $this->deleteOtherSessionRecords(); Notification::make() ->success() ->title('All other browser sessions have been logged out successfully.') ->send(); } private function deleteOtherSessionRecords(): void { /** @var User $user */ $user = Auth::user(); if (config(key: 'session.driver') !== 'database') { return; } DB::connection(config(key: 'session.connection'))->table(table: config(key: 'session.table', default: 'sessions')) ->where('user_id', $user->getAuthIdentifier()) ->where('id', '!=', request()->session()->getId()) ->delete(); } }