Added ability to logout from all other sessions in CP

This commit is contained in:
Pinga 2025-02-11 11:58:50 +02:00
parent 37b9d0ad9e
commit 276cbb1c0d
5 changed files with 63 additions and 3 deletions

View file

@ -13,6 +13,7 @@ use Pinga\Auth\ResetDisabledException;
use Pinga\Auth\TokenExpiredException;
use Pinga\Auth\TooManyRequestsException;
use Pinga\Auth\UserAlreadyExistsException;
use Pinga\Auth\UnknownIdException;
use RobThree\Auth\TwoFactorAuth;
use RobThree\Auth\Providers\Qr\BaconQrCodeProvider;
@ -373,6 +374,21 @@ class Auth
}
}
/**
* Log out user from all other sessions except the current one
* @throws \Pinga\Auth\AuthError
*/
public static function logoutEverywhereElse() {
$auth = self::$auth;
try {
$auth->logOutEverywhereElse();
redirect()->route('profile')->with('success', 'Logged out from all other devices');
}
catch (NotLoggedInException $e) {
redirect()->route('login')->with('error', 'You are not logged in');
}
}
/**
* @throws \Pinga\Auth\AuthError
*/

View file

@ -7,6 +7,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Container\ContainerInterface;
use RobThree\Auth\TwoFactorAuth;
use RobThree\Auth\Providers\Qr\BaconQrCodeProvider;
use App\Auth\Auth;
class ProfileController extends Controller
{
@ -235,4 +236,34 @@ class ProfileController extends Controller
}
}
public function logoutEverywhereElse(Request $request, Response $response)
{
global $container;
$db = $container->get('db');
$currentDateTime = new \DateTime();
$currentDate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp
$db->insert(
'users_audit',
[
'user_id' => $_SESSION['auth_user_id'],
'user_event' => 'user.logout.everywhere',
'user_resource' => 'control.panel',
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'user_ip' => get_client_ip(),
'user_location' => get_client_location(),
'event_time' => $currentDate,
'user_data' => json_encode([
'remaining_session_id' => session_id(),
'logged_out_sessions' => 'All other sessions terminated',
'previous_ip' => $_SESSION['previous_ip'] ?? null,
'previous_user_agent' => $_SESSION['previous_user_agent'] ?? null,
'timestamp' => $currentDate,
])
]
);
Auth::logoutEverywhereElse();
}
}

View file

@ -41,8 +41,8 @@
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
<div class="row row-deck row-cards">
<div class="col-12">
{% include 'partials/flash.twig' %}
<div class="row row-cards">
{% if registrars %}
<div class="{% if minimum_data == 'false' %}col-sm-6 col-lg-3{% else %}col-sm-4 col-lg-4{% endif %}">
@ -254,7 +254,6 @@
</div>
</div>
</div>
</div>
{% include 'partials/footer.twig' %}
</div>
{% if registrars %}

View file

@ -38,6 +38,9 @@
<li class="nav-item">
<a href="#tabs-webauthn" class="nav-link" data-bs-toggle="tab">WebAuthn</a>
</li>
<li class="nav-item">
<a href="#tabs-security" class="nav-link" data-bs-toggle="tab">{{ __('Security') }}</a>
</li>
<li class="nav-item">
<a href="#tabs-log" class="nav-link" data-bs-toggle="tab">{{ __('Log') }}</a>
</li>
@ -203,6 +206,16 @@
{% endif %}
{% endif %}
</div>
<div class="tab-pane" id="tabs-security">
<h3 class="card-title">{{ __('Security') }}</h3>
<p>{{ __('If youve logged in on multiple devices or browsers and want to ensure your account remains secure, you can log out from all other sessions except this one. This will end access from any other logged-in devices.') }}</p>
<form action="{{route('profile.logout.everywhere')}}" name="logoutEverywhere" method="post">
{{ csrf.field | raw }}
<button type="submit" class="btn btn-danger">
{{ __('Log Out from All Devices') }}
</button>
</form>
</div>
<div class="tab-pane" id="tabs-log">
<h3 class="card-title">{{ __('User Audit Log') }}</h3>
<p>{{ __('Track and review all user activities in your account below. Monitor logins, profile changes, and other key actions to ensure security and transparency.') }}</p>

View file

@ -149,6 +149,7 @@ $app->group('', function ($route) {
$route->get('/profile', ProfileController::class .':profile')->setName('profile');
$route->post('/profile/2fa', ProfileController::class .':activate2fa')->setName('activate2fa');
$route->post('/profile/logout-everywhere', ProfileController::class . ':logoutEverywhereElse')->setName('profile.logout.everywhere');
$route->get('/webauthn/register/challenge', ProfileController::class . ':getRegistrationChallenge')->setName('webauthn.register.challenge');
$route->post('/webauthn/register/verify', ProfileController::class . ':verifyRegistration')->setName('webauthn.register.verify');