Weave 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

Laravel Locking Session Laravel Package

rairlie/laravel-locking-session

Laravel package for locking user sessions to a single device or browser, preventing concurrent logins and duplicate active sessions. It helps secure accounts by enforcing one active session per user with configurable middleware and session handling.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require rairlie/laravel-locking-session
php artisan vendor:publish --provider="Rairlie\LockingSession\LockingSessionServiceProvider"

The package auto-discovers its service provider. Your first three moves:

  1. Read the source before the docs. The whole package is a handful of files — a middleware and a session-handler decorator wrapping Laravel's StartSession. You can read it all in ten minutes, and you should, because the config surface is small and the behavior is entirely in those two places.
  2. Open the published config (config/locking-session.php) and confirm the exact key names for the lock store, wait timeout, and lock TTL. Those three knobs are the entire operational story.
  3. Register the middleware on the web group, not globally.

The canonical first use case: a checkout or wizard page that fires four AJAX requests on load, each of which writes to the session (cart row, step counter, flash message, CSRF-adjacent state). Without locking, the last response to finish wins and you silently lose writes. With this middleware installed, those four requests queue behind each other and the session stays coherent.

Validate it immediately: open the app, hit a page with parallel XHRs, and watch the network waterfall. Sequential response completion on the same session is the signal it's working.

Implementation Patterns

Scope the middleware narrowly first. Enable it for your authenticated app routes, not web globally, until you've measured the latency cost. Anonymous traffic — marketing pages, bots, health checks — gains nothing from serialization and will queue up behind itself.

Separate read-only traffic. The main production pattern is to keep exclusive locks for state-mutating routes and let GET polling endpoints (/notifications/count, /api/feed) bypass the middleware entirely. If the package exposes a shared/read lock mode, use it for those; otherwise route-exclude them. Otherwise a 2-second poller will serialize your whole UI behind itself.

Acquire late, release early. Anything slow that doesn't need the session — S3 uploads, third-party API calls, PDF generation — should happen outside the locked window. If you can't restructure, at minimum call session()->save() and release before the slow block, so the next request isn't waiting on your HTTP client.

Frontend coalescing still matters. Session locking fixes correctness; it doesn't fix the fact that you're firing four requests. Debounce and batch anyway — locking turns a data-corruption bug into a latency bug, and you'd rather not have the latency bug.

Test with real concurrency. Http::fake() and single-process feature tests will never catch this. Write a test that opens two parallel curl handles against the same session cookie, has both increment a session counter, and asserts the value is 2. Run it with and without the middleware to prove the middleware is doing anything.

Octane and long-running workers. If you run Laravel Octane, verify the lock is released in the middleware's termination path, not just on normal response. A leaked lock under Octane is a sticky 500 for one user until TTL expiry.

Gotchas and Tips

  • Multi-node deployments need a shared lock store. This is the number one failure mode. If the lock backend is filesystem-based and you run three app servers behind a load balancer, you are locking nothing at all — requests land on different nodes and never contend. Point the lock store at Redis or your database. Verify by running your parallel-write test against staging with more than one node.
  • Tune the wait timeout below your proxy's timeout. A lock wait longer than nginx's fastcgi_read_timeout (or your ALB idle timeout) produces a 504 instead of a clean error, and the request that did hold the lock keeps running invisibly. Set the wait timeout to something like 3–5 seconds and let it fail loudly.
  • Self-deadlock is real. Any code path where a request makes an HTTP call back into your own app using the same session cookie will deadlock until the timeout. This shows up with internal API aggregation and with certain debug toolbars. Audit for Http:: calls to your own hostname.
  • Session ID regeneration changes the lock key. If the key derives from the session ID and you regenerate mid-request (login, privilege escalation), confirm the middleware doesn't release a lock it no longer owns. Test the login flow specifically.
  • Last release was August 2023. Check the composer.json constraints against your Laravel version and run your suite on the target version before committing. If the package doesn't support your Laravel release, the fallback is a ~40-line middleware around Cache::lock("session:{$id}")->block(...) — the concept is trivial, and this package's value is mostly that it's already wired correctly.
  • Debugging stuck requests: add logging around acquire/release in the middleware. When a user reports "the page just spins," you want to know instantly whether it's a lock wait or a genuinely slow query.
  • Extension points: the config-driven lock store is the intended seam for swapping backends; the middleware itself is a fine place to hang metrics (lock wait duration, timeout count) since you're already subclassing or decorating it. Wire the timeout count to your alerting — it's the leading indicator that you've scoped the middleware too broadly.
  • Don't lock the world. If lock-contention metrics climb, the fix is almost always narrower middleware scope, not a bigger timeout.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core