laravel / breeze 読む

BreezeはLaravelで使える認証パッケージ

https://github.com/laravel/breeze
> Breezeは、認証機能を備えたLaravelアプリケーションを構築するための最小限かつシンプルな出発点を提供します。


## インストール

```
$ composer require laravel/breeze --dev
$ php artisan breeze:install
$ yarn 
$ yarn dev
```

## route:list
インストール時にroutes/auth.phpが作成され、routes/web.phpでrequireされて読み込まれます。
認証関係のrouteが追加されます。

```
+--------+----------+---------------------------------+---------------------+-------------------------------------------------------------------------+--------------+
| Domain | Method   | URI                             | Name                | Action                                                                  | Middleware   |
+--------+----------+---------------------------------+---------------------+-------------------------------------------------------------------------+--------------+
|        | GET|HEAD | /                               |                     | Closure                                                                 | web          |
|        | GET|HEAD | api/user                        |                     | Closure                                                                 | api          |
|        |          |                                 |                     |                                                                         | auth:api     |
|        | POST     | confirm-password                |                     | App\Http\Controllers\Auth\ConfirmablePasswordController@store           | web          |
|        |          |                                 |                     |                                                                         | auth         |
|        | GET|HEAD | confirm-password                | password.confirm    | App\Http\Controllers\Auth\ConfirmablePasswordController@show            | web          |
|        |          |                                 |                     |                                                                         | auth         |
|        | GET|HEAD | dashboard                       | dashboard           | Closure                                                                 | web          |
|        |          |                                 |                     |                                                                         | auth         |
|        | POST     | email/verification-notification | verification.send   | App\Http\Controllers\Auth\EmailVerificationNotificationController@store | web          |
|        |          |                                 |                     |                                                                         | auth         |
|        |          |                                 |                     |                                                                         | throttle:6,1 |
|        | POST     | forgot-password                 | password.email      | App\Http\Controllers\Auth\PasswordResetLinkController@store             | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | GET|HEAD | forgot-password                 | password.request    | App\Http\Controllers\Auth\PasswordResetLinkController@create            | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | POST     | login                           |                     | App\Http\Controllers\Auth\AuthenticatedSessionController@store          | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | GET|HEAD | login                           | login               | App\Http\Controllers\Auth\AuthenticatedSessionController@create         | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | POST     | logout                          | logout              | App\Http\Controllers\Auth\AuthenticatedSessionController@destroy        | web          |
|        |          |                                 |                     |                                                                         | auth         |
|        | POST     | register                        |                     | App\Http\Controllers\Auth\RegisteredUserController@store                | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | GET|HEAD | register                        | register            | App\Http\Controllers\Auth\RegisteredUserController@create               | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | POST     | reset-password                  | password.update     | App\Http\Controllers\Auth\NewPasswordController@store                   | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | GET|HEAD | reset-password/{token}          | password.reset      | App\Http\Controllers\Auth\NewPasswordController@create                  | web          |
|        |          |                                 |                     |                                                                         | guest        |
|        | GET|HEAD | verify-email                    | verification.notice | App\Http\Controllers\Auth\EmailVerificationPromptController@__invoke    | web          |
|        |          |                                 |                     |                                                                         | auth         |
|        | GET|HEAD | verify-email/{id}/{hash}        | verification.verify | App\Http\Controllers\Auth\VerifyEmailController@__invoke                | web          |
|        |          |                                 |                     |                                                                         | auth         |
|        |          |                                 |                     |                                                                         | signed       |
|        |          |                                 |                     |                                                                         | throttle:6,1 |
+--------+----------+---------------------------------+---------------------+-------------------------------------------------------------------------+--------------+
```


## register
登録ページは↓のrouteが使われる。

```
GET|HEAD App\Http\Controllers\Auth\RegisteredUserController@create
```

登録リクエストには↓が使われる

```
POST App\Http\Controllers\Auth\RegisteredUserController@store   
```

store処理ではvalidate, login, homeへのリダイレクトが行われる。

```php

    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|confirmed|min:8',
        ]);

        Auth::login($user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]));

        event(new Registered($user));

        return redirect(RouteServiceProvider::HOME);
    }
```

### Auth::login

`Auth` ファサードでAuthManagerが返される。login関数が定義されていないので__callマジックメソッドが実行され、guardのlogin関数が実行される。

```php

    public function __call($method, $parameters)
    {
        return $this->guard()->{$method}(...$parameters);
    }
```

$this->guard()ではドライバーが返される。$nameが指定されていないのでデフォルトドライバーが返される。
Authのデフォルトドライバーはconfigのauth.defaults.guardで指定されている。構築直後ではguardにweb(SessionDriver)が指定されている。
SessionDriverの実クラスはSessionGuardになる。

```php
    public function guard($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

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

    public function getDefaultDriver()
    {
        return $this->app['config']['auth.defaults.guard'];
    }
```

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

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],
```

#### SessionGuard->login()

`Auth::login` ではSessionGuardのlogin()が実行される。
AuthenticatableContractインターフェースを受け取り、セッション更新, remember処理が行われる。

```php

    public function login(AuthenticatableContract $user, $remember = false)
    {
        $this->updateSession($user->getAuthIdentifier());

        // If the user should be permanently "remembered" by the application we will
        // queue a permanent cookie that contains the encrypted copy of the user
        // identifier. We will then decrypt this later to retrieve the users.
        if ($remember) {
            $this->ensureRememberTokenIsSet($user);

            $this->queueRecallerCookie($user);
        }

        // If we have an event dispatcher instance set we will fire an event so that
        // any listeners will hook into the authentication events and run actions
        // based on the login and logout events fired from the guard instances.
        $this->fireLoginEvent($user, $remember);

        $this->setUser($user);
    }
```

#### $this->updateSession
putでセッションストアのプロパティにユーザーidを追加する。
migrate(true)でセッションの削除とセッションidの再設定を行う。

```php

    protected function updateSession($id)
    {
        $this->session->put($this->getName(), $id);

        $this->session->migrate(true);
    }
```

#### $this->ensureRememberTokenIsSet
RememberTokenがない場合にリメンバートークンを設定する

```php

    protected function ensureRememberTokenIsSet(AuthenticatableContract $user)
    {
        if (empty($user->getRememberToken())) {
            $this->cycleRememberToken($user);
        }
    }

    protected function cycleRememberToken(AuthenticatableContract $user)
    {
        $user->setRememberToken($token = Str::random(60));

        $this->provider->updateRememberToken($user, $token);
    }
```

## login
↓でログイン処理が行われる。

```
POST  App\Http\Controllers\Auth\AuthenticatedSessionController@store
```

```php

    public function store(LoginRequest $request)
    {
        $request->authenticate();

        $request->session()->regenerate();

        return redirect(RouteServiceProvider::HOME);
    }
```

ログイン処理はLoginRequestのauthenticateで行われる。
RateLimitチェックとattempt実行を行う。

```LoginRequest.php
    public function authenticate()
    {
        $this->ensureIsNotRateLimited();

        if (! Auth::attempt($this->only('email', 'password'), $this->filled('remember'))) {
            RateLimiter::hit($this->throttleKey());

            throw ValidationException::withMessages([
                'email' => __('auth.failed'),
            ]);
        }

        RateLimiter::clear($this->throttleKey());
    }
```

### Auth::attempt
SessionGuardのattemptが実行される。
プロバイダーからuserを取得して、hasValidCredentialsで資格情報のチェックし、問題なければloginする

```php

    public function attempt(array $credentials = [], $remember = false)
    {
        $this->fireAttemptEvent($credentials, $remember);

        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);

        // If an implementation of UserInterface was returned, we'll ask the provider
        // to validate the user against the given credentials, and if they are in
        // fact valid we'll log the users into the application and return true.
        if ($this->hasValidCredentials($user, $credentials)) {
            $this->login($user, $remember);

            return true;
        }

        // If the authentication attempt fails we will fire an event so that the user
        // may be notified of any suspicious attempts to access their account from
        // an unrecognized user. A developer may listen to this event as needed.
        $this->fireFailedEvent($user, $credentials);

        return false;
    }
```

providerのvalidateCredentials関数で資格情報のバリデーションを行う。

```php
    protected function hasValidCredentials($user, $credentials)
    {
        $validated = ! is_null($user) && $this->provider->validateCredentials($user, $credentials);

        if ($validated) {
            $this->fireValidatedEvent($user);
        }

        return $validated;
    }
```

DatabaseUserPoviderのvalidateCredentialsでpasswordのチェックを行う。

```php
    public function validateCredentials(UserContract $user, array $credentials)
    {
        return $this->hasher->check(
            $credentials['password'], $user->getAuthPassword()
        );
    }
```

## logout
↓でログアウト処理を行う

```
POST App\Http\Controllers\Auth\AuthenticatedSessionController@destroy
```

webガードのlogout関数を実行しセッションの後始末をする。

```php

    public function destroy(Request $request)
    {
        Auth::guard('web')->logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();

        return redirect('/');
    }
```