Chron's avatar
Level 7

Database Session creates another record after logging out

I'm using Laravel w/ Fortify

Here's my test:

Session model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Session extends Model
{
    public $incrementing = false;
    protected $keyType = 'string';
    public $timestamps = false;
}

Here's the session.destroy route

public function destroy(Request $request) {
        auth()->logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();
}

This is the output:

Illuminate\Support\Collection^ {#3000
  #items: array:2 [
    "" => "F0ScGbrKvVlCrHukgZkzxOaZFAvkoJuQTPIRiyus"
    1 => "C1oz9Neg28Cki4PEYTvlXDgoxJ0um7h4zLUNePHs"
  ]
  #escapeWhenCastingToString: false
}

Is this intended when testing database sessions? It changes to null but it creates another record with a user_id.

0 likes
2 replies
imrandevbd's avatar
imrandevbd
Best Answer
Level 6

Yeah, this is perfectly normal and expected behavior.

When you call $request->session()->invalidate(), Laravel flushes the current session data and immediately regenerates a fresh session ID for security (preventing session fixation attacks). Since the user is now logged out, that new session belongs to a guest which is why you see the new record with a null (or empty string) user_id.

The reason your old session record (user_id = 1) is still sitting in the database is because Laravel handles database session cleanup via a lottery-based garbage collection process under the hood (config('session.lottery')). It doesn't always run a hard DELETE query on the old row at the exact millisecond of logout. In a testing environment, that garbage collection lifecycle rarely triggers.

Don't assert against the raw database row count for sessions because of framework quirks like this. Your $this->assertGuest() check is already the correct and most reliable way to test that the logout was successful.

1 like
krisi_gjika's avatar

this line is wrong for many reasons

$this->assertNotEquals(0, Session::whereUserId($this->user->id)->count());

not only will it fail for any other session driver than database, but you shouldn't be testing framework internals at all. What your test should care is if user is authenticated or not after hitting each endpoint, not if the internal framework session handling works.

1 like

Please or to participate in this conversation.