ログイン中のLaravelユーザーでwordpressへ自動ログインする

さてさて、前回 Laravelのユーザーでwordpressへログインできるようにする では、Laravelに登録されているユーザー情報を使ってwordpressへログインする方法を紹介しました。

このような使い方をすることで、例えばLaravelで開発されたサイトであっても各ユーザーがwordpressで開設された共同ブログにログインし、ユーザー全員で情報発信することができるようになります。

ただこの場合、ひとつある課題が発生してしまいます。

それは・・・・・・すでにLaravelのサイトにログインしたのに、再度ブログページでログインを要求される、つまりユーザビリティがあまり良くない状況になってしまうわけです。

そこで!

今回は独自プラグインlaravel-auto-loginを作成して、すでにLaravelでログインしている場合は自動的にwordpressにログインができるようにしてみます。

ぜひ参考にしてみてくださいね。

※ 開発環境: Laravel 5.7、wordpress 5.0

自動ログインの手順

おおまかに自動ログインは次の手順で行います。

  1. Laravelのユーザーデータが自動ログイン用のトークンを保持する
  2. wordpressでそのトークンを使って認証をする
  3. 自動ログインを実行する

では、実際に開発していきましょう!

Laravel側の作業

自動ログイン用トークンのためのフィールドを追加する

まずはLaravelのユーザー情報を保存しているテーブルにauto_login_tokenという名前のフィールドを追加します。

今回はすでにあるマイグレーションdatabase/migrations/****_**_**_******_create_users_table.phpを変更します。

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->string('auto_login_token')->nullable();
        $table->rememberToken();
        $table->timestamps();
    });
}

設定が終わったら、マイグレーションを実行しておいてください。
テーブル構造はこのようになります。

自動ログイン・トークンのイベントを作成する

まず、auto_login_tokenフィールドは、次のような使い方をします。

  • ログインしたら新しい文字列を保存
  • ログアウトしたら空にする

そのため、これらを実現するためにログイン&ログアウト時に実行されるイベントを作っていきます。

まずはapp/Providers/EventServiceProvider.phpを開いて、次のようにログインとログアウトに実行されるイベントを登録します。

// app/Providers/EventServiceProvider.php

protected $listen = [
    'Illuminate\Auth\Events\Authenticated' => [
        'App\Listeners\LogAuthenticated',
    ],
    'Illuminate\Auth\Events\Logout' => [
        'App\Listeners\LogSuccessfulLogout',
    ],
    
    // 省略

];

そして、次のコマンドを実行してイベントリスナーを作成しましょう。

php artisan event:generate

すると、app/Listenersフォルダの中に以下2つのファイルが作成されていますので、これら2つのファイル内にコードを書き込んでいきます。

  • LogAuthenticated.php
  • LogSuccessfulLogout.php

※ なお、ログイン&ログアウト時のイベントを詳しく知りたい方は、選べる全4種類!Laravelで最終ログイン&ログアウト日時を保存(たった4行〜イベントまで)をご覧ください。

LogAuthenticated

ログインが成功したときのイベント 認証が成功したときのイベントですので、次のよう新しいauto_login_tokenを登録します。

public function handle(Authenticated $event)
{
    $user = $event->user;
    $user->auto_login_token = $user->id .'-'. str_random(32);
    $user->save();
}

これでログインしたら各ユーザーに一意な文字列が保存されることになります。

※ この方法は、ログインしてページ移動すると毎回呼ばれます。もしログイン時に1回だけ実行したい場合はLoginController.phpauthenticated()で実装してください。詳しくは選べる全4種類!Laravelで最終ログイン&ログアウト日時を保存をご覧ください。

LogSuccessfulLogout

ログアウトした時に実行するイベントです。
auto_login_tokenを初期化します。

public function handle(Logout $event)
{
    $user = $event->user;
    $user->auto_login_token = null;
    $user->save();
}

ログアウトすると、次のように初期化されます。

自動ログインURLを作成する

では、Laravel側最後の作業として、自動ログインURLを作成します。

なお、簡単にどこからでも自動ログインURLを取得できるようにUserモデルにaccessorを作ります。

// app/User.php

class User extends Authenticatable
{
    // 省略

    // Accessor
    public function getAutoLoginUrlAttribute() {

        return 'http://wordpress.example.com/laravel-auto-login?token='. $this->auto_login_token;

    }

}

これで、$user->auto_login_urlとすると次のような「自動ログインURL」が取得できるようになります。

http://wordpress.example.com/laravel-auto-login?token=1-rDvmDmQTrlFDx1NqHgOZkK4fGYRW0vGn

なお、laravel-auto-loginというページは、これからプラグインで作成する自動ログイン専用のページです。

wordpress側の作業

続いてwordpress側の作業です。

注意点

今回もLaravel側のDBに直接アクセスするコードを書いていきますが、もし前回記事で作った独自プラグインlaravel-authenticationを使っている場合はデータベース接続情報の部分はwp-config.phpへ移動しておいてください。(こうしないと定義が重複しているとしてエラーが発生します)

// wp-config.php

// LaravelのDB情報
define('LARAVEL_DB_TYPE', 'mysql');
define('LARAVEL_DB_HOST', 'localhost');
define('LARAVEL_DB_PORT', '3306');
define('LARAVEL_DB_DATABASE', 'YOUR-LARAVEL-DB-NAME');
define('LARAVEL_DB_USERNAME', 'YOUR-USER-NAME');
define('LARAVEL_DB_PASSWORD', 'YOUR-PASSWORD');
define('LARAVEL_DB_USER_TABLE', 'users'); // 名前が違う場合は変えてください

独自プラグイン開発の準備

wp-content/pluginsフォルダの中にlaravel-auto-loginフォルダを作成し、その中にlaravel-auto-login.phpを作成します。

独自プラグインをつくる

では、今回の記事でメインになる独自プラグインのコードです。

<?php
/*
Plugin Name: Laravel Auto Login
Description: A plugin that allows to auto-login by Laravel users.
Version: 1.0.0
Author: Sukohi Kuhoh
Author URI: https://blog.capilano-fw.com
*/

add_filter('wp', 'laravel_auto_login');

function laravel_auto_login()
{
    global $wp;

    // 自動ログイン専用ページかどうかをチェック
    if($wp->request == 'laravel-auto-login') {

        // DB接続
        try {

            $pdo = new PDO(
                LARAVEL_DB_TYPE . ':' .
                'host=' . LARAVEL_DB_HOST . ';' .
                'port=' . LARAVEL_DB_PORT . ';' .
                'dbname=' . LARAVEL_DB_DATABASE . ';' .
                'charset=utf8mb4', // DSN
                LARAVEL_DB_USERNAME,
                LARAVEL_DB_PASSWORD
            );

        } catch (Exception $e) {

            throw new Exception('Failed to connect to database.');

        }

        $token = trim($_GET['token']);

        // トークンを使ってLaravelユーザーを取得
        $sql = $pdo->prepare('SELECT id,name,email FROM ' . LARAVEL_DB_USER_TABLE . ' WHERE auto_login_token=:token');
        $sql->bindParam(':token', $token);
        $sql->execute();
        $laravel_user = $sql->fetch();

        if ($laravel_user) {

            $laravel_name = $laravel_user['name'];
            $laravel_email = $laravel_user['email'];
            $password = md5(uniqid(rand(), 1));

            $user_id = null;
            $wp_user = get_user_by('email', $laravel_email);

            if (!$wp_user) { // ユーザー登録

                $user_id = wp_insert_user([
                    'user_login' => md5(uniqid(rand(), 1)),
                    'user_pass' => $password,
                    'user_email' => $laravel_email,
                    'display_name' => $laravel_name,
                    'role' => 'author'
                ]);

            } else { // ユーザー更新

                $user_id = $wp_user->ID;
                wp_update_user([
                    'ID' => $user_id,
                    'user_pass' => $password,
                    'user_email' => $laravel_email,
                    'display_name' => $laravel_name
                ]);

            }

            // ユーザーIDを使ってログイン
            wp_set_current_user($user_id);
            wp_set_auth_cookie($user_id);

            // ダッシュボードへリダイレクト
            wp_redirect('wp-admin/');
            exit();

        }

        // ここまででログインできなかったら失敗。Laravelへリダイレクト 
        wp_redirect('http://laravel.example.com/login'); // ご自身のURLへ変更してください  
        exit();

    }

}

【追記:2019.01.26】コードに間違いがありましたので変更しました。

では内容をひとつずつ説明していきます。

自動ログイン専用ページかどうかをチェック

まず、このプラグインは全ページで呼び出されることになります。
そして、自動ログイン専用ページのURL(つまり、http://******/laravel-auto-login)にアクセスがあった場合だけコードを実行することになります。

DB接続

データベースの接続は前回のlaravel-authenticationと同じくPDOです。
ただし、違っているのはDB接続に失敗した場合は例外処理を実行するようにしています。

トークンを使ってLaravelユーザーを取得

続いて、パラメータに含まれているトークンからLaravel側のユーザーデータを取得します。

ユーザー登録&ユーザー更新

もしLaravelのユーザーが見つかった場合、wordpressに同じユーザーが存在しているかをチェックし、なければ新規登録。あればそのユーザーデータを更新するようにします。

なお、ここの処理はほとんど前回記事と同じですが、それぞれユーザーIDを取得している部分が違います。これは後でユーザーをログインさせるために必要になるからです。

また、パスワード情報は取得できないのでランダムな文字列を使っています。

ユーザーIDを使ってログイン

wordpress側にユーザーが準備できたら、そのユーザーIDを使ってログインさせます。

ダッシュボードへリダイレクト

wordpressにログインさせたら、ダッシュボードへリダイレクトします。

※ なお、もし自動ログインに失敗したらLaravelのログインページへ移動するようにしています。

プラグインを有効化する

では、準備は整いましたのでプラグインを有効化しましょう。
プラグインページで次のリンクをクリックします。

テストしてみる

では、準備はすべて整ったのでテストしてみましょう。
まず、Laravelresources/views/home.blade.php内に自動ログインのリンクを作成してログインします。

<a href="{{ auth()->user()->auto_login_url }}">自動ログイン</a>

するとこのようにリンクが表示されます。

クリックしてみます。

うまく自動ログインできました!

今回は以上になります。
お疲れ様でした。

おまけ – 1(セキュリティを向上させる)

ちなみに追加するかどうか迷った結果、できるだけ根本的な部分を紹介するために省いた機能をおまけとして紹介します。

今回紹介した内容だと、アクセストークンはログインしたときにだけ更新されることになります。そのため、可能性としてはその後長い間トークンが同じままになる場合もあるわけです。これはセキュリティ上あまりいいとは言えません。

【追記: 2019.01.26】 LogAuthenticatedを使って実装している場合、ログイン中にページ移動があれば毎回自動ログインのトークンは更新されます。内容が正確ではなく申し訳ございません。そのため、以下の内容はもしLoginController.phpで実装していた場合に追加する内容としてお読みください。

そのため、自動ログイン・トークンが使えるのは一回だけにし、さらにログインしていればページ移動する度にトークンを更新するようにしてみます。

ページを移動する度にトークンを更新する

まず、Laravel側です。

ページにアクセスする度にログインをチェックするためにUpdateAutoLoginTokenという名前のミドルウェアを作りましょう。

php artisan make:middleware UpdateAutoLoginToken

中身はこうなります。(太字が追加した部分です)

public function handle($request, Closure $next)
{
    if(auth()->check()) {

        $user = $request->user();
        $user->auto_login_token = $user->id .'-'. str_random(32);
        $user->save();

    }

    return $next($request);
}

そして、このミドルウェアをapp/Http/Kernel.phpに登録します。

protected $middlewareGroups = [
    'web' => [

        // 省略

        \App\Http\Middleware\UpdateAutoLoginToken::class
    ],

    // 省略

];

これで、ログイン状態であればページ移動したときに必ず自動ログイン・トークンを更新するようになります。

自動ログインしたらトークンを削除する

次にwordpress側です。
自動ログインが成功した時点でトークンを削除します。

// ユーザーIDを使ってログイン

// 省略

// Laravel側の自動ログイントークンを初期化
$sql = $pdo->prepare('UPDATE '. LARAVEL_DB_USER_TABLE .' SET auto_login_token = :token');
$sql->bindValue(':token', null, PDO::PARAM_NULL);
$sql->execute();

// ダッシュボードへリダイレクト

// 省略

これでよりセキュアなサイト構成になりました。

おまけ – 2(トークン更新を一元管理する)

今回は説明のために各場所でトークンを更新するようにしていましたが、複数回呼ばれるコードは一元管理すべきです。そのため、次のような次のようなメソッドを用意するといいでしょう。

// User.php

public function updateAutoLoginToken() {

    $this->auto_login_token = $this->id .'-'. str_random(32);
    $this->save();

}

使い方はこうなります。

$user->updateAutoLoginToken();

おわりに

ということで、前回に引き続きLaravelwordpressを統合する話題をお届けしました。

ちなみに、今回の独自プラグインは前回のlaravel-authenticationと併用できるので、wordpressのログインページからLaravelのユーザー情報でログインすることもできます。

なお、記事中でも言いましたが、define()部分が重複してしまうとPHPエラー(実際にはNotice)が発生してしまうので、wp-config.phpへ移動させました。

もちろん我々プログラマーだけが使う分にはこの形でもいいのでしょうが、通常のユーザーが使う場合にはファイルを開いて編集というのは少し怖かったりもします。

そのため、もし気になるようでしたらwordpressの設定ページでDBへの接続情報を編集できるようにするといいでしょう。(気が向いたらこの部分も記事にしてみたいと思いますね)

ではでは〜!