Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Session Concurrency Laravel Package

ajgl/session-concurrency

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup in Laravel

  1. Installation

    composer require ajgl/session-concurrency
    

    (Note: This package is Symfony-focused, but can be adapted for Laravel via custom authentication logic.)

  2. First Use Case: Basic Concurrency Check

    • Override Laravel’s default session handling by extending Illuminate\Auth\SessionGuard or using middleware.
    • Inject the concurrency strategy into your auth logic:
      use Ajgl\SessionConcurrency\Strategy\ConcurrencyControlStrategy;
      use Ajgl\SessionConcurrency\Strategy\CompositeStrategy;
      
      $concurrencyStrategy = new ConcurrencyControlStrategy($maxSessions);
      $defaultStrategy = new \Symfony\Component\Security\Core\Authentication\Strategy\SessionAuthenticationStrategy();
      $compositeStrategy = new CompositeStrategy([
          $concurrencyStrategy,
          $defaultStrategy,
      ]);
      
  3. Where to Look First

    • src/Strategy/: Core strategies (e.g., ConcurrencyControlStrategy).
    • src/EventListener/: SessionRegistryExpirationListener for session cleanup.
    • Symfony Bundle: If migrating to Symfony later, reference AjglSessionConcurrencyBundle.

Implementation Patterns

Workflow: Integrating with Laravel Auth

  1. Middleware Approach Use middleware to wrap auth checks:

    namespace App\Http\Middleware;
    
    use Ajgl\SessionConcurrency\Strategy\ConcurrencyControlStrategy;
    use Closure;
    
    class CheckSessionConcurrency
    {
        protected $strategy;
    
        public function __construct(ConcurrencyControlStrategy $strategy)
        {
            $this->strategy = $strategy;
        }
    
        public function handle($request, Closure $next)
        {
            if (!$this->strategy->supports($request)) {
                return $next($request);
            }
    
            if (!$this->strategy->authenticate($request)) {
                return redirect()->route('login')->with('error', 'Max sessions reached.');
            }
    
            return $next($request);
        }
    }
    

    Register in app/Http/Kernel.php:

    protected $middlewareGroups = [
        'web' => [
            // ...
            \App\Http\Middleware\CheckSessionConcurrency::class,
        ],
    ];
    
    
  2. Session Storage Hooks Extend Laravel’s SessionGuard to log sessions:

    use Illuminate\Auth\SessionGuard;
    use Ajgl\SessionConcurrency\SessionRegistry;
    
    class CustomSessionGuard extends SessionGuard
    {
        protected $registry;
    
        public function __construct(SessionRegistry $registry, $request)
        {
            $this->registry = $registry;
            parent::__construct($request);
        }
    
        public function login($user)
        {
            $this->registry->add($this->session()->getId(), $user->id);
            return parent::login($user);
        }
    }
    
  3. Event-Driven Session Cleanup Listen for Illuminate\Session\Events\Starting to sync sessions:

    use Ajgl\SessionConcurrency\EventListener\SessionRegistryExpirationListener;
    use Illuminate\Support\Facades\Event;
    
    Event::listen('Illuminate\Session\Events\Starting', function () {
        $listener = new SessionRegistryExpirationListener(
            $maxSessions,
            $sessionRegistry
        );
        $listener->onKernelResponse();
    });
    

Integration Tips

  • Laravel Session Driver: Ensure your SESSION_DRIVER (e.g., file, database) supports session ID persistence.
  • User Model: Store session counts in a sessions table or cache (e.g., Redis) for performance.
  • Testing: Mock SessionRegistry to test concurrency logic:
    $registry = $this->createMock(SessionRegistry::class);
    $registry->method('countForUser')->willReturn(3);
    $strategy = new ConcurrencyControlStrategy(2, $registry);
    $this->assertFalse($strategy->authenticate($request));
    

Gotchas and Tips

Pitfalls

  1. Session ID Mismatches

    • Laravel’s default session ID generation may conflict with Symfony’s expectations. Override session()->getId() if needed:
      $sessionId = $request->session()->getId() ?: session_id();
      
  2. Race Conditions

    • Concurrent login() calls can lead to duplicate session entries. Use transactions or locks:
      DB::transaction(function () use ($user, $registry) {
          $registry->add($sessionId, $user->id);
      });
      
  3. Symfony Dependencies

    • The package expects Symfony’s Security\Core\Authentication\Token\TokenInterface. Adapt Laravel’s Authenticatable:
      use Symfony\Component\Security\Core\User\UserInterface;
      
      class LaravelUser implements UserInterface
      {
          // Implement Symfony's UserInterface methods.
      }
      

Debugging

  • Log Session Registry Add debug output to SessionRegistry:

    public function add($sessionId, $userId)
    {
        \Log::debug("Added session {$sessionId} for user {$userId}");
        // ...
    }
    
  • Check Event Listeners Ensure SessionRegistryExpirationListener is triggered:

    Event::listen('kernel.response', function () {
        \Log::debug('Kernel response event fired');
    });
    

Extension Points

  1. Custom Expiration Logic Override SessionRegistryExpirationListener to implement soft/logged-out sessions:

    class CustomExpirationListener extends SessionRegistryExpirationListener
    {
        protected function expireOldSessions($userId, $maxSessions)
        {
            // Custom logic (e.g., notify user via email).
            parent::expireOldSessions($userId, $maxSessions);
        }
    }
    
  2. Dynamic Max Sessions Fetch $maxSessions from a config or user role:

    $maxSessions = config("auth.max_sessions.{$user->role}");
    $strategy = new ConcurrencyControlStrategy($maxSessions, $registry);
    
  3. IP/Device Fingerprinting Enhance SessionRegistry to track devices:

    public function add($sessionId, $userId, $request)
    {
        $this->sessions[$userId][$sessionId] = [
            'ip' => $request->ip(),
            'user_agent' => $request->userAgent(),
        ];
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky