[Laravel]異なるサイト間でCookieとSessionを共有してログイン状態を保持する


概要

異なるサイト間でCookieSessionを共有してログイン状態を保持する手順を記載します。
片方のサイトでログインすればもう片方もログイン状態になり、片方でログアウトすればもう片方もログアウトするような感じです。
Laravelで以下の条件を満たしていれば、基本的に設定ファイルを編集するだけでできます。

  • サイト間で認証方式が同じ
  • サイト間でドメインが同じ
  • サイト間で同じデータベースを使用している

今回は、Laravelでデフォルトの認証方式である$ php artisan make:authを両方のプロジェクトで実行したとして話を進めます。
独自の認証だったり他のライブラリ等を利用した場合は、設定に関する変更箇所を適宜見つける必要があります。

Cookie/Sessionとは

ステートレス(状態を持たない)であるHTTP通信において、ログイン状態などの状態を保持しておきたいときにCookieSessionを用います。
Cookieはクライアント側で持つ情報、Sessionはサーバー側で持つ情報です。
これらの情報は連想配列のように取得することが可能です。

参考:【PHP超入門】Cookieとセッションについて

環境

Laravel 5.5.45
Homestead 8.2.0
Vagrant 2.2.4
VirtualBox 7.2.1

手順

Cookieをサブドメイン間で共有する

1.認証ドライバの設定

ガードにsessionを指定します。
$ php artisan make:authのコマンドで作成した認証方法であれば、あらかじめ設定されているため変更不要ですが、内容を確認してみます。
ガード(Guard)とは、Authの認証に利用しているドライバークラスで、各リクエストごとにどのようにユーザーを認証するかを定義します。

各プロジェクトでconfig/auth.phpを開き、webのガードが認証ドライバとしてsessionを指定していることを確認します。

config/auth.php
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

また、デフォルトでwebのガードが使用されていることを確認します。

config/auth.php
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

2.APP_URLの設定

.envファイルのAPP_URLを設定します。
各プロジェクトで、正しいURLを設定してください。

- APP_URL=http://localhost
+ APP_URL=http://shared-session.example.test

2.ドメインの設定

Cookieのドメインの設定をします。
ドメインを指定した場合、Cookieはそのドメイン自体だけでなく、サブドメインに対しても送信されます。

Cookieの仕様で、.comや.jpといったトップレベルドメインに対してはdomain属性は設定しても無効になります。
また、ブラウザによっては.co.jpなど一般的なドメインも無効になる場合があります。

各プロジェクトの.envファイルにSESSION_DOMAINを追加し、サイトで使用しているドメインを指定します。

SESSION_DRIVER=file
SESSION_LIFETIME=120
+ SESSION_DOMAIN=.example.test

この定数はconfig/session.php で読み込まれています。
.envファイルにSESSION_DOMAINが存在しない場合にnullを返しています。

config/session.php
    'domain' => env('SESSION_DOMAIN', null),

3.Cookie名の設定

Cookie名を指定します。
各プロジェクトの.envファイルにSESSION_COOKIEを追加し、任意のCookie名を指定します。

SESSION_DRIVER=file
SESSION_LIFETIME=120
+ SESSION_COOKIE=shared_cookie

もしくは、各プロジェクトの.envファイルのAPP_NAMEを同じにします。
config/session.phpで、SESSION_COOKIEがない場合にAPP_NAMEを使用するよう指定しているためです。

APP_NAME=Laravel
config/session.php
    'cookie' => env(
        'SESSION_COOKIE',
        str_slug(env('APP_NAME', 'laravel'), '_').'_session'
    ),

4.Cookieを複合するためのキーの設定

各プロジェクトの.envファイルでAPP_KEYを同じにします。
Cookieに含まれてるセッションIDは暗号化されているため、同一キーでないと複合できないためです。
片方のをコピーして、もう片方のプロジェクトへ貼り付けます。

+ APP_KEY=base64:+UeQnKa......=

5.確認

Cookieがサブドメイン間で共有されていることを確認していきます。
サーバーを再起動し、両方のサイトでログインします。

ブラウザがChromeの場合、 右上のメニューアイコン > 設定 > 詳細設定 > コンテンツの設定 > Cookie > すべての Cookie とサイトデータを表示 を開きます。
参考:Chromeでcookieを確認する方法: 小粋空間

検索バーで「example.test」を検索します。
Cookieがサブドメインごとではなく、指定したドメインに対して作成されています。

クリックすると、SESSION_COOKIEで指定したCookie名になっています。

参考

Sessionをデータベースで共有する

1.Sessionの保存先を設定

Sessionの保存先をデータベースに変更します。
各プロジェクトの.envファイルでSESSION_DRIVERをfileからdatabaseに変更します。

- SESSION_DRIVER=file
+ SESSION_DRIVER=database

この定数はconfig/session.php で読み込まれています。

config/session.php
  'driver' => env('SESSION_DRIVER', 'file'),

2.Sessionのテーブルを作成

以下のコマンドでSession用のマイグレーションファイルを作成し、実行してSessionテーブルを作成します。
この操作はどちらか片方のプロジェクトでOKです。

$ php artisan session:table
$ php artisan migrate

3.確認

これでSessionの共有は完了です。
片方でサイトでログインしてみて、もう片方のサイトでリロードするとログイン状態になれば成功です。

また、データベースでSessionテーブルへアクセスしてみてセッションのデータが作成されているか確認してみてください。

$ mysql -u root -p
Enter password:
mysql> show databases;
mysql> use <table名>;
mysql> show tables;
mysql> select * from sessions;

参考

補足

うまく行かない場合

以下の方法等を試してください。

  • 設定を片方のプロジェクトのみしていないか確認する
  • storage/framework/sessionsフォルダ内の不要なセッションファイルを消してみる
  • ブラウザのCookie情報を消してみる (右上のメニューアイコン > 設定 > 詳細設定 >閲覧履歴データを削除する > Cookieとキャッシュの項目をチェックして「データを削除」をクリック)
  • サーバーを再起動してみる (Homesteadを使用してる場合は $ vagrant reload —provision を実行)

セキュリティについて

CookieとSessionを共有することに関してのセキュリティについて考えます。
Cookieに関しては、domainを指定することによってCookieが送信される範囲が広がるため、挙動を理解しておく必要があります。
CookieのDomain属性は 指定しない が一番安全 | 徳丸浩の日記
Cookie - サブドメイン間のCookieの共有|teratail

また、CookieとSessionを共有することによって片方の情報が漏洩すること=もう片方も漏洩することになるので、漏洩した場合に影響範囲が広くなります。

複数のデータベースを使用している場合

以下が参考になります。

ドメインが全く異なる場合

シングルサインオン(SSO)を使用すると実現できると思います。

最後に

記事に間違いや不明な点があれば遠慮なくご指摘ください。