diff --git a/src/Services/Users/SafelyDestroyUserSessionTrait.php b/src/Services/Users/SafelyDestroyUserSessionTrait.php index b8c1a2a..ec2999a 100644 --- a/src/Services/Users/SafelyDestroyUserSessionTrait.php +++ b/src/Services/Users/SafelyDestroyUserSessionTrait.php @@ -2,6 +2,7 @@ namespace EvolutionCMS\UserManager\Services\Users; +/** Removes authentication state for one context without destroying unrelated session data. */ trait SafelyDestroyUserSessionTrait { private $userSessionFields = [ @@ -19,6 +20,12 @@ trait SafelyDestroyUserSessionTrait 'Token', ]; + /** + * Clears this context's identity and, for manager logout, its CSRF credential. + * Web-context logout must not invalidate a still-authenticated manager session. + * + * @return void + */ protected function safelyDestroyUserSession() { if (defined('NO_SESSION')) { @@ -28,5 +35,8 @@ protected function safelyDestroyUserSession() foreach ($this->userSessionFields as $field) { unset($_SESSION[$this->context . $field]); } + if ($this->context === 'mgr') { + unset($_SESSION['_token']); + } } } diff --git a/src/Services/Users/UserLogin.php b/src/Services/Users/UserLogin.php index 94c66e8..1223844 100644 --- a/src/Services/Users/UserLogin.php +++ b/src/Services/Users/UserLogin.php @@ -8,6 +8,10 @@ use Carbon\Carbon; use Illuminate\Support\Str; +/** + * Validates login credentials and creates the authenticated context's session. + * Manager logins rotate CSRF credentials without discarding unrelated web-session data. + */ class UserLogin implements UserServiceInterface { use SafelyDestroyUserSessionTrait; @@ -280,7 +284,7 @@ public function authProcess() EvolutionCMS()->cleanupExpiredLocks(); EvolutionCMS()->cleanupMultipleActiveUsers(); - if(!defined('NO_SESSION')) { + if (!(class_exists('EvoSessionProxy', false) && \EvoSessionProxy::disabled())) { $this->writeSession(); } // successful login so reset fail count and update key values @@ -337,6 +341,12 @@ public function writeSession() } + /** + * Creates a fresh native session after login, preserving unrelated session data. + * Manager authentication also rotates CSRF; a web login keeps the manager token. + * + * @return string Newly assigned session identifier. + */ protected function regenerateSessionId(): string { $sessionId = Str::random(40); @@ -354,12 +364,24 @@ protected function regenerateSessionId(): string $_SESSION[$key] = $value; } + if ($this->context === 'mgr') { + // A new authenticated manager session must not inherit its predecessor's token. + $_SESSION['_token'] = Str::random(40); + } + $this->syncLaravelSessionId($sessionId); $this->refreshSessionCookie($sessionId); return $sessionId; } + /** + * Mirrors the native session ID and rotated manager token into Laravel when enabled. + * Native session state remains authoritative if no compatible store is available. + * + * @param string $sessionId Newly generated native session identifier. + * @return void + */ protected function syncLaravelSessionId(string $sessionId): void { if (!defined('EVO_SESSION') || !EVO_SESSION || !function_exists('session')) { @@ -370,6 +392,9 @@ protected function syncLaravelSessionId(string $sessionId): void $store = session()->driver(); if (is_object($store) && method_exists($store, 'setId')) { $store->setId($sessionId); + if ($this->context === 'mgr' && method_exists($store, 'put')) { + $store->put('_token', $_SESSION['_token']); + } } } catch (\Throwable $exception) { // Native PHP session remains the source of truth when Laravel sync is unavailable.