skip to content

In Laravel, how does Auth::logoutOtherDevices() sign a user out elsewhere, and why does it depend on the auth.session middleware?

level: seniorimportance: should knowfreq 30%

answer

  1. needs the current password
  2. force-rehashes: same password, new hash
  3. password_hash_web kept in each session
  4. auth.session compares it every request
  5. authenticateSessions() adds it to web

basics

~20 s

Auth::logoutOtherDevices($password) checks the current password and re-hashes it, changing the stored hash. The auth.session middleware (AuthenticateSession) compares each session's saved password-hash marker with the user's current hash, so other sessions stop matching and are logged out on their next request.

solid answer

~40 s

At login the session guard stores an HMAC of the user's password hash in the session as `password_hash_<guard>`. `Auth::logoutOtherDevices($currentPassword)` verifies the password with `Hash::check` (throwing `InvalidArgumentException` if it is wrong), then asks the provider to rehash it with `force: true`, so the same password gets a new hash with a new salt. It re-queues this device's remember-me cookie so it matches, and fires `OtherDeviceLogout`. Nothing contacts the other sessions directly: the `auth.session` middleware (`Illuminate\Session\Middleware\AuthenticateSession`) compares the stored marker with the user's current hash on every request, and on a mismatch calls `logoutCurrentDevice()`, flushes the session and throws `AuthenticationException`. Remembered devices fail too, because their cookie's HMAC no longer matches. So the middleware must be on the routes, via `->middleware('auth.session')` or `$middleware->authenticateSessions()` in `bootstrap/app.php`. Without it, other sessions carry on.

code

php · 15 lines
php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;

Route::middleware(['auth', 'auth.session'])->group(function () {
    Route::post('/account/sign-out-elsewhere', function (Request $request) {
        $request->validate(['password' => ['required', 'current_password']]);

        Auth::logoutOtherDevices($request->password);

        return back()->with('status', 'Signed out of your other devices.');
    });
});

go deeper

for a junior

Know that Auth::logoutOtherDevices() takes the current password and signs the user out of their other sessions.

for a middle

Explain the password_hash marker in each session, the forced rehash, and why AuthenticateSession must run for other devices to notice.

for a senior

Wire auth.session correctly, including on the calling route, handle the wrong-password exception, and know password changes trigger the same effect.

for a principal

Decide what 'sign out everywhere' must guarantee, including admin-initiated revocation, and whether the session driver supports enumerating sessions per user.

## The problem it solves A member reports that they signed into the gym kiosk app on a friend's phone and forgot to sign out. Changing the password is one answer; `Auth::logoutOtherDevices()` is the lighter one: **end every other session, keep this one**. ## How Laravel marks sessions When `SessionGuard::login()` runs, besides the user ID it stores `password_hash_<guard>` in the session: an HMAC-SHA256 of the user's current password hash, keyed with `APP_KEY`. Every session of that user, on every device, carries the same marker, because it is derived from the same stored hash. ## What `logoutOtherDevices()` changes `Auth::logoutOtherDevices($password)` on the session guard: 1. returns immediately if nobody is signed in; 2. checks `Hash::check($password, $user->getAuthPassword())` and throws `InvalidArgumentException` ("The given password does not match the current password.") on a mismatch; 3. calls the provider's `rehashPasswordIfRequired(..., force: true)`: the **same** password is hashed again, with a new salt, and saved, so the stored hash changes; 4. if this request has a remember-me cookie, re-queues it with the new hash, so this device stays remembered; 5. fires `Illuminate\Auth\Events\OtherDeviceLogout`. No other session is touched. The change is in the user record. ## Where the sign-out actually happens The `auth.session` middleware alias maps to `Illuminate\Session\Middleware\AuthenticateSession`. On each request with a signed-in user it: - if the user was restored from a remember-me cookie, checks the cookie's hash segment against the current password hash; - stores the marker if the session has none yet; - compares the session's `password_hash_<guard>` with an HMAC of the user's current hash; - on mismatch: `logoutCurrentDevice()`, `session()->flush()`, and throws `AuthenticationException`, which redirects guests to the login route by default; - after the response, stores the marker again from the current hash. | Device | Stored marker after the call | Next request through `auth.session` | |---|---|---| | This device | refreshed after the response, when `auth.session` ran on the calling route | passes | | Friend's phone | still the old HMAC | signed out | | Laptop with remember-me | cookie hash is old | signed out | ## Wiring the middleware Either add it to the routes that need it, `Route::middleware(['auth', 'auth.session'])->group(...)`, or enable it for the whole `web` group in `bootstrap/app.php` with `$middleware->authenticateSessions()`. ## Traps - **No `auth.session`, no effect.** Other sessions never compare the marker, so they stay signed in until they expire. - **Put it on the route that calls it too.** The current session's marker is refreshed by the middleware's after-response step; if the route that calls `logoutOtherDevices()` lacks `auth.session`, this session keeps the old marker and is signed out on its next protected request. - **Wrong password throws.** Catch `InvalidArgumentException`, or validate first with the `current_password` rule, and show a form error. - **Password changes behave the same way.** Any new password hash makes the marker stale, so with `auth.session` in place a password change signs out other sessions even without calling this method. - **All guards.** The docs note the other sessions are invalidated entirely, so a user signed into several guards on another device loses all of them there.

  • Why does logoutOtherDevices() need the user's current password?
    It re-hashes that password to change the stored hash, and it verifies it first with `Hash::check`, throwing `InvalidArgumentException` if it does not match. That also makes the action a proof of knowledge, so someone at an unattended signed-in screen cannot lock the real owner out of their other devices.
  • How would you make every session of a user end when an admin disables the account?
    `logoutOtherDevices()` only works for the signed-in user with their password, so it does not fit. With the `database` session driver you can delete that user's rows from the `sessions` table, and cycling `remember_token` stops remember-me cookies; checking an account flag in middleware is the other common approach.

saying these in an interview costs you the question

  • logoutOtherDevices() deletes the other sessions from the session store directly.
  • It works without any middleware because the guard handles it.
  • The user's password is changed to a new random value.
  • logoutOtherDevices() quietly does nothing when given a wrong password.
  • Remember-me cookies on other devices keep working afterwards.