Laravelのログイン認証条件を追加しつつログイン維持条件も追加する

# やりたいこと

- Laravelの認証処理 `php artisan make:auth` を使用
- 作成される `User` テーブルに無効フラグを追加し、無効なユーザーではログインさせない

# やったこと

とりあえずググってみた結果、以下の内容がよく出てきた。

- 認証対象の項目を指定しているのは `AuthenticatesUsers` トレイトの `credentials`メソッド
- `LoginController` で `credentials` メソッドをオーバーライドして条件追加

``` php:LoginController.php

use Illuminate\Http\Request;

class LoginController extends Controller
{
    protected function credentials(Request $request)
    {
        $arr = $request->only($this->username(), 'password');
		$arr['disable'] = 0;	// 無効フラグOFFの条件を追加

		return $arr;
    }
}

```
できた～と思ったら…

# 問題点

「**ログイン状態を維持**」でログインした場合、
その後対象ユーザーの無効フラグを立ててもログイン状態が維持される。
（一度ログアウトすると再ログインは不可になる）

## 調査結果

[Laravelの実装調査　～ログイン編・リメンバーログインもあるよ～](https://qiita.com/t-kuni/items/eb0d2c1970dfab5f6abe#%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E7%8A%B6%E6%85%8B%E3%81%AE%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF)

この記事によれば `SessionGuard` の `userFromRecaller` で処理されてるらしい。
更に追いかけると`UserProvider` の `retrieveByToken` の中が肝っぽい。
`config` 配下を確認すると

``` php:auth.php

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
    ],

```
`driver` が `eloquent` なので実装は `Illuminate\Auth\EloquentUserProvider.php`

``` php:EloquentUserProvider.php

<?php

// ・・・・

class EloquentUserProvider implements UserProvider
{
    public function retrieveByToken($identifier, $token)
    {
        $model = $this->createModel();

		// UserモデルからIDで引っ張る
        $model = $model->where($model->getAuthIdentifierName(), $identifier)->first();

        if (! $model) {
            return;
        }

		// rememberTokenが一致すればOK
        $rememberToken = $model->getRememberToken();

        return $rememberToken && hash_equals($rememberToken, $token) ? $model : null;
    }

```

`ID` と `rememberToken` しか見てなかった。。。
クッキーさえ残っていればいつまでもログイン状態になってしまうので困る。

# じゃあどうするか

`UserProvider` 自分でカスタマイズしたほうがよさそう。
あ、`LoginController`は元に戻しました。

## カスタムUserProvider作成

``` php:CustomEloquentUserProvider.php

<?php

namespace App\Auth;

use Illuminate\Support\Str;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable;

class CustomEloquentUserProvider extends EloquentUserProvider
{
	// rememberToken認証のやつ
    public function retrieveByToken($identifier, $token)
    {
        $model = $this->createModel();

		$model = $model->where($model->getAuthIdentifierName(), $identifier)
						->where('disable', 0)	// disable指定を追加
						->first();

        if (! $model) {
            return;
        }

        $rememberToken = $model->getRememberToken();

        return $rememberToken && hash_equals($rememberToken, $token) ? $model : null;
	}
	

	// ログイン時のやつ
	public function retrieveByCredentials(array $credentials)
    {
        if (empty($credentials) ||
           (count($credentials) === 1 &&
            array_key_exists('password', $credentials))) {
            return;
        }

        $query = $this->createModel()->newQuery();

        foreach ($credentials as $key => $value) {
            if (! Str::contains($key, 'password')) {
                $query->where($key, $value);
            }
        }

		// disable指定を追加
        return $query->where('disable', 0)->first();
    }
}

```

## 認証サービスの登録

``` php:AuthServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Application;

use App\Auth\CustomEloquentUserProvider;

class AuthServiceProvider extends ServiceProvider
{
	/**
     * アプリケーションのためにマップ付けるポリシー
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
	];
	
    /**
     * 認証／認可サービスの登録
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

		// 追加
        Auth::provider('custom_eloquent', function (Application $app, array $config) {
            return new CustomEloquentUserProvider($app['hash'], $config['model']);
        });
    }
}

```

## config に追加

``` php:auth.php

    'providers' => [
        'users' => [
			// 'driver' => 'eloquent',
			'driver' => 'custom_eloquent',	// カスタマイズ
            'model' => App\User::class,
        ],
    ],

```

# 結果

- 「ログイン状態を維持」でもセッションが切れたら再ログインが必要になった。ﾔｯﾀﾈ

- セッションも強制的に切りたいなら `SessionGuard.php` の `user()` メソッドのオーバーライドになるかな…？

- `LoginController` の `credentials` オーバーライドはちょっと使い道が思いつかない。
ログイン時はチェックするけど、リメンバーログインではチェックしない条件とかあるんなら使えそう。
または「ログイン状態を維持」を使用しないとか。
どちらにせよ`UserProvider`の継承で対応できるから敢えて選ぶメリットはない気がする。