Laravelのmiddleware authを理解したい（6.x）

以前書いた、[Laravelのmiddleware authを理解したい (5.5)](https://qiita.com/darum/items/d53924177a7285a5c152)から、約2年経過しましたが、LTSも新バージョンが出ているので、差分を理解するという意味も含めて6.xについて調べてみることにしました。

例によって、[Laravelのmiddleware authを理解したい](https://qiita.com/washio12/items/59f5cde23b4205973c6b) と同じ構成です（感謝です！）

# 環境
Laravel 6.18.6 ([laravel/framework](https://github.com/laravel/framework) のSHA: 4ef5f9612c2694c915f53f0cec3e4081e9bfc778)

# 調査

---
※ `make:auth` artisanコマンドがなくなりました！

[マニュアル](https://readouble.com/laravel/6.x/ja/authentication.html#authentication-quickstart)より、

```
composer require laravel/ui "^1.0" --dev

php artisan ui vue --auth
```
とやることで、vue.jsを利用したスカルフォードが生成される。

---
自動生成コードによれば、認証が必要なものはMiddlewareで定義する。

```Http/Controllers/HomeController.php
class HomeController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }
```

- `Http/Kernel.php` にて、「auth」というaliasを登録している。

```Http/Kernel.php
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
```

# `\Illuminate\Auth\Middleware\Authenticate` でやっていること


- [ガードを指定](https://readouble.com/laravel/6.x/ja/authentication.html#protecting-routes) することで、認証の方式を変更できる

```\Illuminate\Auth\Middleware\Authenticate.php
    public function handle($request, Closure $next, ...$guards)
    {
        $this->authenticate($request, $guards);

        return $next($request);
    }
```
- 上記の`$guards` に、指定された文字列が入る（`$this->middleware('auth:web.api');` のように複数指定が可能なので可変引数
- `Illuminate/Auth/AuthManager`で処理を行う
 -  `interafce Illuminate\Contracts\Auth\Factory` が、DIされているが、下記の通り実態は`AuthManager`

```\Illuminate\Auth\Middleware\Authenticate.php
// use Illuminate\Contracts\Auth\Factory as Auth; とされている
    /**
     * Create a new middleware instance.
     *
     * @param  \Illuminate\Contracts\Auth\Factory  $auth
     * @return void
     */
    public function __construct(Auth $auth)
    {
        $this->auth = $auth;
    }
```

```/Illuminate/Foundation/Application.php
    public function registerCoreContainerAliases()
    {
        foreach ([
            'app'                  => [self::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class, \Psr\Container\ContainerInterface::class],
            'auth'                 => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
```

- `$guard`で指定された分だけ、Guardを使用して、認証済みか確認（`/Illuminate/Auth/SessionGuard::check()`）し、認証済みのGuardの使用を宣言する
 - ここで、`config/auth.php` で指定がなかったguardsは、Exceptionが発生する
 - 指定可能なdriverは、「session」「token」とのことだが、ここでは省略
 - `$guard`を指定していない場合は、nullとして処理され、AuthManagerで、`config/auth.php`の、defaultsが指定される。初期設定ではweb → 本資料では、このパターンについて理解を進める

# `/Illuminate/Auth/SessionGuard::check()` が呼ばれるまで
## 呼んでいる箇所

```/Illuminate/Auth/Middleware/Authenticate.php
    protected function authenticate($request, array $guards)
    {
        if (empty($guards)) {
            $guards = [null];
        }

        foreach ($guards as $guard) {
            if ($this->auth->guard($guard)->check()) {
                return $this->auth->shouldUse($guard);
            }
        }
```

## `$this->auth->guard()` でオブジェクトが特定されるまで

```/Illuminate/Auth/AuthManager.php
    public function guard($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
    }
```

- 今回は`$name = null`のため、`$name = 'session'` として処理

```/Illuminate/Auth/AuthManager.php
    protected function resolve($name)
    {
        $config = $this->getConfig($name);

        if (is_null($config)) {
            throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
        }

        if (isset($this->customCreators[$config['driver']])) {
            return $this->callCustomCreator($name, $config);
        }

        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';

        if (method_exists($this, $driverMethod)) {
            return $this->{$driverMethod}($name, $config);
        }

        throw new InvalidArgumentException(
            "Auth driver [{$config['driver']}] for guard [{$name}] is not defined."
        );
    }
```

- AuthManage.php::resolve() で、使用するGuardを決定

## AuthManager::createSessionDriver() で、Guardが生成される

```/Illuminate/Auth/AuthManager.php
    /**
     * Create a session based authentication guard.
     *
     * @param  string  $name
     * @param  array  $config
     * @return \Illuminate\Auth\SessionGuard
     */
    public function createSessionDriver($name, $config)
    {
        $provider = $this->createUserProvider($config['provider'] ?? null);

        $guard = new SessionGuard($name, $provider, $this->app['session.store']);
```
- 実体は`/Illuminate/Auth/SessionGuard`

## `AuthManager::createUserDriver()` でやっていること
（実装は、`trait /Illuminate/Auth/CreateUserProviders`）

[5.2解説](https://qiita.com/washio12/items/59f5cde23b4205973c6b#createuserprovider%E3%81%8C%E3%82%84%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%93%E3%81%A8)より、

- `config/auth.php`の、`auth.providers.{$provider}` の値 → `auth.providers.users`の値を取得する
    - デフォルト：`auth.providers.users = ['driver'=>'eloquent','model'=>'App\User']`
- 「何を使って、どこからUser情報を取得するか」というのを取得する。
- デフォルトでは、「eloquentを使って、App\Userモデルの情報をとる」ということ
- Laravelでは、driverに以下の設定が可能
    - `eloquent`
        - Eloquent Modelを使ってユーザを特定
        - `model`で、どのModelを使用するか指定
        - `Illuminate\Auth\EloquentUserProvider` を使う
    - `database`
        - データベースを使う
        - `table`でテーブル名を指定
        - `Illuminate\Auth\DatabaseUserProvider` を使う
- `interface Illuminate\Contracts\Auth\UserProvider`の主なメソッドは以下のとおり
    - retrieveById()：IDで認証する
    - retrieveByToken()：token(remember me)で認証する
    - updateRememberToken()：token(remember me)を更新する
    - retrieveByCredentials()：credentialを更新する
    - validateCredentials()：credentialを利用してユーザを検証する

# `new SessionGuard()`でやっていること

```Illuminate\Auth\SessionGuard.php
    public function __construct($name,
                                UserProvider $provider,
                                Session $session,
                                Request $request = null)
    {
        $this->name = $name;
        $this->session = $session;
        $this->request = $request;
        $this->provider = $provider;
    }
```

- [従来と変わらず](https://qiita.com/darum/items/d53924177a7285a5c152#new-sessionguard%E3%81%A7%E3%82%84%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8)

# `SessionGuard::check()` でやっていること

`trait \Illuminate\Auth\GuardHelpers`に実装

```\Illuminate\Auth\GuardHelpers
    /**
     * Determine if the current user is authenticated.
     *
     * @return bool
     */
    public function check()
    {
        return ! is_null($this->user());
    }
```

- `user()` を使って、認証されているかを返す

## `SessionGuard::user()` の処理

- [5.5と同様](https://qiita.com/darum/items/d53924177a7285a5c152#user-%E3%81%8C%E3%82%84%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8)

---

フレームワークの根幹に関わるところなので、基本的な処理は変わっていない印象です。（メソッド名とかクラス名が少し変わってるけど、流れは変わっていない）
参考になればと思います。