Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Services/Users/SafelyDestroyUserSessionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace EvolutionCMS\UserManager\Services\Users;

/** Removes authentication state for one context without destroying unrelated session data. */
trait SafelyDestroyUserSessionTrait
{
private $userSessionFields = [
Expand All @@ -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')) {
Expand All @@ -28,5 +35,8 @@ protected function safelyDestroyUserSession()
foreach ($this->userSessionFields as $field) {
unset($_SESSION[$this->context . $field]);
}
if ($this->context === 'mgr') {
unset($_SESSION['_token']);
}
}
}
27 changes: 26 additions & 1 deletion src/Services/Users/UserLogin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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')) {
Expand All @@ -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.
Expand Down