[Laravel] パスワード変更時に他のデバイスでログインしているアカウントをログアウトさせる

Laravelの認証システムを利用していて、パスワード変更時に他のデバイスでログインしているアカウントを強制的にログアウトさせたいと思ったことはありませんか?

この問題を解決するために5.4移行のバージョンには\Illuminate\Session\Middleware\AuthenticateSessionミドルウェアが実装されています。

\App\Http\Kernelから抜粋 (デフォルトではコメントアウトされていて無効になっています)

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

使い方

\App\Http\Kernelのコメントアウトを外すだけです。

それだけでパスワード変更時に他のデバイスでログインしているアカウントを強制的にログアウトさせることができます。

仕組み

\Illuminate\Session\Middleware\AuthenticateSessionhandleメソッドを見ていきましょう。

ユーザーがまだ認証されていない場合 ($request->user()メソッドでログインを試みる) や、セッションが無効な場合 (APIなど) にはミドルウェアを通過します。

if (! $request->user() || ! $request->session()) {
    return $next($request);
}

Laravelの認証システムではremember meの機能が標準で用意されています。

この仕組みはブラウザのCookieを利用し、Cookie情報の中に暗号化したユーザーの認証情報を保持しています。

  • ユーザーの識別番号 (ユーザーID等)
  • rememberトークン
  • ユーザーのハッシュパスワード

ここではCookieからパスワードハッシュを取得し、現在のユーザーのパスワードハッシュと比較します。一致しない場合(ユーザーがパスワードを変更した)、認証されたユーザーをログアウトします。

if ($this->auth->viaRemember()) {
    $passwordHash = explode('|', $request->cookies->get($this->auth->getRecallerName()))[2];

    if ($passwordHash != $request->user()->getAuthPassword()) {
        $this->logout($request);
    }
}

セッションにパスワードハッシュがない場合、認証されたユーザーのパスワードハッシュがセッションに格納されます。次の部分ではなぜそれをするのか説明します。

if (! $request->session()->has('password_hash')) {
    $this->storePasswordHashInSession($request);
}

セッションに保存されているパスワードハッシュが認証されたユーザーのパスワードハッシュと一致しない場合(ユーザーがパスワードを変更した)、認証されたユーザーをログアウトします。

ここでは、パスワードを変更した後でも、他のデバイスでのログインセッションは継続しています。

if ($request->session()->get('password_hash') !== $request->user()->getAuthPassword()) {
    $this->logout($request);
}

最後に、リクエストが終了し結果を返す前に、セッションにパスワードハッシュを保存します。

もう一度やり直す理由は、ユーザーがそのリクエスト内でパスワードを変更した可能性があり、その結果新しいパスワードハッシュが生成される可能性があるからです。

パスワードハッシュを保存していないと、パスワードを変更した後にシステムからログアウトさせられます。

return tap($next($request), function () use ($request) {
    $this->storePasswordHashInSession($request);
});

このように\Illuminate\Session\Middleware\AuthenticateSessionミドルウェアは、セッション認証を利用し、ユーザーのパスワードが変更されるたびにシステムからログアウトされます。

また、ブラウザに残っている古いCookie情報からのログインも無効になります。

© Xzxzyzyz