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 Single Session Laravel Package

overtrue/laravel-single-session

Prevent concurrent logins in Laravel by limiting each user to a single active session. When a user signs in from a new device or browser, earlier sessions are invalidated automatically, keeping accounts secure with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Check compatibility before you type composer require. The last release was 2017-12-11 and the repo is archived. Its illuminate/* constraints target Laravel 5.4/5.5 and PHP 5.6/7.0; on Laravel 6+ Composer will fail dependency resolution outright, and on Laravel 5.6–5.8 it will install but may collide with the framework's own session middleware. If you are on anything newer than 5.5, skip to the native alternative in Implementation Patterns — the package will not receive fixes for the session-handling changes made after 5.6.

Assuming a Laravel 5.5 app:

composer require overtrue/laravel-single-session

On 5.5 the Overtrue\LaravelSingleSession\ServiceProvider is auto-discovered. On 5.4 add it manually to the providers array in config/app.php.

Where to look first: src/, not the README. For a 17-star package from 2017 the source is the documentation. Open the service provider first and answer three questions before going further:

  1. What does it bind or listen to on Illuminate\Auth\Events\Login (or wherever it hooks)?
  2. Where does it persist the "current session id" — the users table, the cache, or a session flash? This determines whether it works on your infrastructure at all (see Gotchas).
  3. What is the middleware class's fully-qualified name? You will need it for Kernel.php.

Then register the middleware, scoped to authenticated web routes rather than globally:

// app/Http/Kernel.php
protected $routeMiddleware = [
    // ...
    'single.session' => \Overtrue\LaravelSingleSession\Middleware\SingleSession::class,
];

First use case to validate: log in as the same user in browser A, then in browser B. Refresh A — it should be redirected to the login page. If A stays authenticated and B stays authenticated, the middleware is registered but not running (check route:list --columns=Middleware), or the package is comparing session IDs from before Laravel regenerated them on login.

Implementation Patterns

Route-group scoping, not global. Applying this globally will hit your api group, health checks, and any token-authenticated route, where there is no session to compare. Attach it to the authenticated portion of your web routes:

Route::middleware(['web', 'auth', 'single.session'])->group(function () {
    Route::get('/dashboard', 'DashboardController@index');
});

Verify it is guard-aware. Most packages of this vintage hardcode Auth::guard() (the default guard). If you have a multi-guard setup (admin, customer), confirm the middleware resolves the correct guard — otherwise logging in as an admin will evict a customer session that happens to share the user id.

Pair it with remember-token rotation. Killing the stale session does not kill the stale remember-me cookie. The old device can silently re-authenticate via Auth::viaRemember(), defeating the whole point. On each new login, rotate the token so old cookies are invalid:

// app/Http/Controllers/Auth/LoginController.php
protected function authenticated(Request $request, $user)
{
    $user->setRememberToken(Str::random(60));
    $user->save();
}

Use it as a signal, not just a wall. Rather than only booting the old session, log the eviction so you have an audit trail and can notify the user ("you signed in from a new device") — the package description advertises this as the motivating use case, but you have to wire the notification yourself off whatever event or log line the middleware emits.

Testing. A standard actingAs() test only ever has one session, so it cannot exercise this. To test properly, drive two requests through the session store with distinct session IDs, or assert on the persisted "current session id" value after a simulated login rather than on the HTTP 302.

If you are on Laravel 5.6+ (recommended): drop the package and use the framework. AuthenticateSession middlewares ships in core and is the maintained equivalent:

// app/Http/Kernel.php, web group
\Illuminate\Session\Middleware\AuthenticateSession::class,

It requires a password_hash column on users (added in 5.6) and invalidates sessions when the password changes — combine it with Auth::logoutOtherDevices($password) on login/password-change to get exactly the behaviour this package promises. For password-change flows there is also Auth::logoutOtherDevices(), which needs the current plaintext password for confirmation.

Gotchas and Tips

Session ID regeneration is the failure mode. Laravel regenerates the session ID inside Auth::login(). If the package (or your wrapper) fingerprints the session before that happens, it stores the pre-login ID and immediately kills the session that just logged in — presenting as an infinite login loop. If you hit that, check the ordering of the login event listener versus $request->session()->regenerate().

The persistence layer is the architecture decision. The "no extra database tables" claim means the current session id lives in the cache or the user row. Either way, CACHE_DRIVER=array or file breaks it across web workers and load-balanced nodes — stale sessions on node B are never invalidated. You need a shared Redis/Memcached/database store, and the same caveat applies to SESSION_DRIVER=file in a multi-server setup.

Stale tabs fail ugly. A user with an open form on the evicted device gets a 419 Page Expired on POST rather than a friendly "signed in elsewhere" message, because the CSRF token went with the session. Handle it in app/Exceptions/Handler.php — detect unauthenticated requests from previously-authenticated sessions and redirect with a message, and return JSON 401 for expectsJson() clients rather than an HTML redirect.

Exclude queue workers, console commands, and API/token routes. These have no session; the middleware will either no-op confusingly or throw on a null session store.

Do not use it for password-change invalidation. It reacts to new logins, not to credential changes. That is a separate concern — logoutOtherDevices() plus the password_hash column.

Debugging checklist, in order: php artisan route:list (is the middleware attached?), log $request->session()->getId() at login and on each request (did it change?), inspect the cache key / user column the package writes, and confirm the cache store is shared. If the ID matches but the user is still logged out, the middleware is comparing against a value written by a different guard.

Extension points are thin on an archived package: publish/override the config if one ships, subclass the middleware and re-register your own in Kernel.php, or listen to the login event yourself and reimplement the check in ~20 lines — which is often the sanest exit if you are already off Laravel 5.5.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin