Introduction

If you have a large ecosystem with multiple applications, it’s nice to have a common user account that you can use to authenticate in these separate applications. So when a user authenticates any of these apps, they will be logged in everywhere.

What you’ll need to do

  • Share the cookies, sessions and users between the applications
  • Use the same encryption key
  • Use the same domain

Create your common database

Here you will only store your common data like your users, sessions, subscriptions, etc. Make sure that all your applications can access this database.

Step 1: Update the database configurations

Add a new database connection

You need to do this in all of your applications.

In your config/database.php in the connections array specify your “common_database” connection. Just copy one of your existing configs.

'common_database' => [
            'driver' => 'mysql',
            'url' => env('COMMON_DATABASE_URL'),
            'host' => env('COMMON_DB_HOST', '127.0.0.1'),
            'port' => env('COMMON_DB_PORT', '3306'),
            'database' => env('COMMON_DB_DATABASE', 'forge'),
            'username' => env('COMMON_DB_USERNAME', 'forge'),
            'password' => env('COMMON_DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

Configure your common models

Edit the models you want to store in the common database and set the connection property.

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    protect $connection = 'common_database';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

You can do this in your migration classes too.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::connection('common_database')->create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

Update your validation rules

If you used validation rules to check the user’s table, you have to update them with the connection name.

For example in your RegisterController.

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;

class RegisteredUserController extends Controller
{
    /**
     * Display the registration view.
     *
     * @return \Illuminate\View\View
     */
    public function create()
    {
        return view('auth.register');
    }

    /**
     * Handle an incoming registration request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:common_database.users',
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        event(new Registered($user));

        Auth::login($user);

        return redirect(RouteServiceProvider::HOME);
    }
}

Step 2: Configure sessions

Basically, you need the same sessions configuration in all of your applications and the same encryption key.

Store the sessions in the database

This command will create a migration for your sessions:

php artisan session:table

Open up your newly created migration file and set the connection as we did before.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateSessionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::connection('common_database')->create('sessions', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->foreignId('user_id')->nullable()->index();
            $table->string('ip_address', 45)->nullable();
            $table->text('user_agent')->nullable();
            $table->text('payload');
            $table->integer('last_activity')->index();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('sessions');
    }
}

Now in your .env file set the session driver and connection.

SESSION_DRIVER=database
SESSION_CONNECTION=common_database

Set your session cookie domain.

You need to set your domain address in the session configuration, so all of your applications will have access to the cookies. It doesn’t matter where you log in, you will be logged in everywhere.

So add this to your .env file:

SESSION_DOMAIN=".example.com"

(Make sure to include a dot before your domain name.)

Update application keys

Make sure the “APP_KEY” variable in your .env file has the same value in all your applications. Just copy one and paste it everywhere.

Thanks 🙂