Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

thewebartisan7's avatar

Database sessions with multi-guard

I am using multi guard with 2 different tables, users and admins.

I have created two separated login and all seem working fine, I can login as user or as admin, or also both.

I am using also the database session driver for store users sessions, which I display in admin area.

The problem that I am facing is that both admins and users are stored in the same database sessions table.

I notice that when I am logged in with the same browser both as admin and as user, then user_id column change if I am on admin side or frontend.

Then I get issue for display the relation between users/sessions and admins/sessions, since there is only a user_id column.

The best would be to create an additional sessions table only for admins, like sessions_admins, but I am not sure how to do this since there Laravel manage this table.

Any ideas?

Thank You.

0 likes
10 replies
jlrdw's avatar

One table is all you need and a user is a user no matter what role.

You use Authentication to make sure someone is logged in.

You use authorization to determine what the logged in user can or cannot do.

Authorization works with the role to to determine who can do what actions.

In his free video series Jeffrey has the basics of Authentication and authorization.

6.0 isn't updated in the series yet the 5.7 series has these videos.

Plus there's the chapter in the documentation one covers authentication and there's another chapter on authorization.

And of course a user can have multiple roles, I have a system an admin is also a bookkeeper.

You can set up a related roles table.

thewebartisan7's avatar

@jlrdw thanks for your reply.

I understand your point, and I use already roles and permissions, and I saw Jeffrey video on auth.

Laravel offers multiple guards for some reasons, and allow separated tables, to handle certain case like mine.

In my case, an admin is different entity that can't do things that a user do.

I can lists many reasons why not, but I think that you understand what I mean.

For example an admin should not be show in browse users, or should not have all columns/data that an user have.

I could do what you are suggesting, I also see that here was posted similar question without a solution: https://laracasts.com/discuss/channels/laravel/multiple-sessions-from-same-browser

But then why Laravel offers multi-guards with multi-tables, if then can't handle multi-sessions table.

Thanks

thewebartisan7's avatar

I change the table of Admin model to the same of User model, but the session.user_id works in the same way.

Of course this happen when there is 2 sessions in the same browser for each guard, and I could just logout other session when a new is created, if there is not another way.

Maybe there is a way to add a sessions.guard_name in sessions table, so that appear two different sessions, and not the same one.

Since Laravel support two sessions like auth()->guard('web')->user() and auth()->guard('admin')->user(), should support also two sessions...

jlrdw's avatar

It's hard to explain this. Don't think of an admin as someone who can do anything. In the bookkeeping system, an admin can't mess with the bookkeeping.

You use a combination of query scopes also to determine certain actions..

An example:

Bob has two roles ==== admin and bookkeeper

Dave has one role === admin

Greg has one role === user

Bob logs in.

  • Bob can do admin actions
  • Bob can update accounts, since he also has bookkeeping role

Dave now logs in

  • Dave can do just the things that an admin is allowed to do
  • Dave cannot mess with bookkeeping

A role is matched with a can do or cannot do.

See this gist: https://gist.github.com/jimgwhit/ed44a6c81815804f1ab910ce9eb88d84

A scope can fine tune a who can do what.

Also see https://laracasts.com/discuss/channels/general-discussion/login-with-two-different-roles

https://laracasts.com/discuss/channels/laravel/security-in-controller

I have custom authorization, but only recommend custom stuff to experienced programmers.

And

A good package is https://github.com/spatie/laravel-permission

thewebartisan7's avatar

@jlrdw thanks for your reply, but I know how to handle the multi roles / permissions using scopes and similar way, and I am using spatie permissions/roles already, which support multi guards, multi models for permissions and roles, so it's perfectly combined with mine scenario with multi guards, and multi users tables.

My question was how to use multi sessions database table in a multi guards scenario.

I think that I found a solution, but I am not yet sure how to implement it.

Looking in a package for impersonate users, I saw how this package extens SessionGuard, see here https://github.com/404labfr/laravel-impersonate/blob/master/src/Guard/SessionGuard.php

I think that I need to do a similar way, and then store admins sessions in a a separated table.

I see how he register it here https://github.com/404labfr/laravel-impersonate/blob/master/src/ImpersonateServiceProvider.php#L123-L147

But need to see how this works.

If you have an idea how to do this, please let me know.

I can handle this with roles/permissions, in fact I have already do that before start implementing multi guards.

Thanks

thewebartisan7's avatar

I think that I am on right road with custom session guard, which then can be added in auth config, auth.guards.{guard_name}.driver(see https://github.com/laravel/laravel/blob/master/config/auth.php#L40)

And the right class to extend should be Illuminate\DatabaseUserProvider.

I should take some time, which I don't have right now, to figure out how to do it. If anyone has an idea, let me know.

I have seen that many other people have asked the question not only here but also elsewhere, but a solution with example is not found, or at least I have not found.

sarukomine's avatar

I have same issue as your, I have tried to create a another session table for admin guard before, but it don't work...

Since the problem is from user_id column, so I need to create a session handler for this case. And it already solved my problom.

  1. Add column to sessions table.
  2. Create a custom session handler.
  3. Create a custom session driver for this custom session handler.

============================================

  • Add web_user_id and admin_user_id instead of user_id
Schema::create('sessions', function (Blueprint $table) {
    $table->string('id')->unique();
    $table->unsignedBigInteger('web_user_id')->nullable();    // <<<
    $table->unsignedBigInteger('admin_user_id')->nullable();  // <<<
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->text('payload');
    $table->integer('last_activity');
});
  • Create DatabaseSessionHandler file in app\Extensions.
<?php

namespace App\Extensions;

use Illuminate\Contracts\Auth\Factory;
use Illuminate\Session\DatabaseSessionHandler as BaseDatabaseSessionHandler;

class DatabaseSessionHandler extends BaseDatabaseSessionHandler
{
    protected function addUserInformation(&$payload)
    {
        if ($this->container->bound(Factory::class)) {
            $payload['web_user_id'] = $this->webUserId();
            $payload['admin_user_id'] = $this->adminUserId();
        }

        return $this;
    }

    protected function webUserId()
    {
        $user = $this->getUser('web');
        
        return $user ? $user->id : null;
    }

    protected function adminUserId()
    {
        $user = $this->getUser('admin');
        
        return $user ? $user->id : null;
    }

    protected function getUser($guard)
    {
        return $this->container->make(Factory::class)->guard($guard)->user();
    }
}
  • Create a session custom driver.

Add following code to app service provider.

use App\Extensions\DatabaseSessionHandler;

public function boot()
{
    Session::extend('NAME_YOU_WANT', function ($app) {
        $table   = config('session.table');
        $minutes = config('session.lifetime');

        return new DatabaseSessionHandler($this->getDatabaseConnection(), $table, $minutes, $app);
    });
}

protected function getDatabaseConnection()
{
    $connection = config('session.connection');

    return DB::connection($connection);
}
  • Finally, change the session driver as a custom session driver
2 likes
Benja's avatar

extend the laravel default DatabaseSessionHandler and override the addUserInformation function

<?php

namespace App\Extensions;

use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Session\DatabaseSessionHandler;

class CustomDatabaseSessionHandler extends DatabaseSessionHandler
{
    /**
     * Add the user information to the session payload.
     *
     * @param array $payload
     * @return $this
     * @throws BindingResolutionException
     */
    protected function addUserInformation(&$payload): static
    {
        if ($this->container->bound(Guard::class)) {
            info(($this->user() ? get_class($this->user()) : null));
            $payload['userable_type'] = $this->user() ? get_class($this->user()) : null;
            $payload['userable_id'] = $this->userId();
        }

        return $this;
    }

    /**
     * Get the currently authenticated user's ID.
     *
     * @return mixed
     * @throws BindingResolutionException
     */
    protected function user(): mixed
    {
        return $this->container->make(Guard::class)->user();
    }
}

then register the driver in AppServiceProvider

public function boot(): void
    {
        Session::extend('custom-database', function ($app) {
            $table = $app['config']['session.table'];
            $lifetime = $app['config']['session.lifetime'];
            $connection = $app['db']->connection($app['config']['session.connection']);

            return new CustomDatabaseSessionHandler($connection, $table, $lifetime, $app);
        });
    }

change your sessions table migration

public function up()
    {
        Schema::create('sessions', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->string('userable_type')->nullable();
            $table->foreignId('userable_id')->nullable()->index();
            $table->string('ip_address', 45)->nullable();
            $table->text('user_agent')->nullable();
            $table->longText('payload');
            $table->integer('last_activity')->index();
        });
    }

then change session driver in your .env file

SESSION_DRIVER=custom-database

now the sessions stored in the database are polymorphic

Please or to participate in this conversation.