Laravel の認証・認可パッケージが多すぎてわけわからんので図にまとめた

# 元ネタ

@localdisk さんの記事です。

https://zenn.dev/localdisk/articles/a338175e913410

こちらで概ね適切に説明されているものの，文章のみで図が無くて直感的に把握しづらいので，初心者にもすぐ飲み込ませられるように図に描き起こしてみました。

# 図

![download-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25060/6dd5d531-32e5-c8c4-c60f-b29cee04b8b8.png)

# 解説

## `illuminate/auth`: 最小限の認証認可コアロジック

https://github.com/illuminate/auth

コアコンポーネント群の `laravel/framework` に含まれているものです。 **<font color=red>Socialite 以外のすべてのパッケージが，実質このコアに依存していることになります。</font>**

以下の記事でこのパッケージの詳細について説明しているので，ここでは端折って説明します。

https://qiita.com/mpyw/items/8c5413b99b8e299f7002

### 伝統的 Cookie ベースのセッション認証

https://qiita.com/mpyw/items/7852213f478e8c5a2802

こちらでも解説している， **「Cookie に識別子を載せ，それに対応する情報はサーバ側のファイルに記録する」** という手法に近いものです。 実装は `illuminate/session` にあり， PHP ネイティブのセッションの挙動を上書きする [SessionHandler](https://www.php.net/manual/ja/class.sessionhandler.php) インタフェースを実装しています。PHP デフォルトだとファイル書き込み一択ですが， Laravel ではドライバとしてファイル以外にも，データベース，Redis あるいは Cookie 自体にデータも暗号化して含める，なども選択できるようになっています。

https://github.com/illuminate/session

Laravel デフォルトの設定で `Auth` ファサードを使うと，このセッション機能を使用する **[SessionGuard](https://github.com/illuminate/auth/blob/5a4e69270a2ac9a73ab2cf767b01bbcc1dd5c2fb/SessionGuard.php
)** が選択されます。


```php
// 新規ログイン
$user = Auth::attempt(['email' => '...', 'password' => '...']);

// ログインしている場合は取得できる
$user = Auth::user();
```

### BASIC 認証

`SessionGuard` が持っている別の機能です。 `Request` は内部的に保持しているようなので，引数なしで呼ぶだけで `Auth::attempt()` と同様の認証できます。

```php
// https://user:password@host:port 形式で URL が指定されたときに user と password を抽出できる
$user = Auth::basic();
```

### 任意ロジック認証

https://qiita.com/mpyw/items/8c5413b99b8e299f7002#requestguard-%E3%81%AE%E5%88%A9%E7%94%A8

上記で解説しているように， `Auth::viaRequest()` を使って `RequestGuard` を使うと，任意の認証ロジックに対応することができます。基本は簡易的にコールバックで済ませますが，自前で `callable` クラスとして定義してもいいし， `UserProvider` だけカスタムで作ってもいいし，あるいは `Guard` ごと自分で作ってもよいです。

> ```php:UserProviderを無視する場合
> Auth::viaRequest('custom-token', function (Request $request): ?User {
>     return User::where('token', $request->token)->first();
> });
> ```
> 
> ```php:UserProviderの仕組みに乗る場合
> Auth::viaRequest('custom-token', function (Request $request, UserProvider $provider): ?User {
>     return $provider->retrieveByCredentials($request->only('token'));
> });
> ```

### メールアドレス検証

以下のクラスが内包されています。

- [auth/MustVerifyEmail.php](https://github.com/illuminate/auth/blob/5a4e69270a2ac9a73ab2cf767b01bbcc1dd5c2fb/MustVerifyEmail.php)
  - `User` モデルに実装する簡易的なトレイト（対応するコントラクトもある）
- [auth/Notifications/VerifyEmail.php](https://github.com/illuminate/auth/blob/5a4e69270a2ac9a73ab2cf767b01bbcc1dd5c2fb/Notifications/VerifyEmail.php)
  - 認証用メール（Laravel の Notification 機能を利用している）
  - メールをカスタムしたい場合は [VerifyEmail::toMailUsing()](https://github.com/illuminate/auth/blob/5a4e69270a2ac9a73ab2cf767b01bbcc1dd5c2fb/Notifications/VerifyEmail.php#L104-L113) を利用する
  - 認証用 URL をカスタムしたい場合は [VerifyEmail::createUrlUsing()](https://github.com/illuminate/auth/blob/5a4e69270a2ac9a73ab2cf767b01bbcc1dd5c2fb/Notifications/VerifyEmail.php#L93-L102) を利用する
- [auth/Middleware/EnsureEmailIsVerified.php](https://github.com/illuminate/auth/blob/5a4e69270a2ac9a73ab2cf767b01bbcc1dd5c2fb/Middleware/EnsureEmailIsVerified.php)
  - メールアドレスが検証済みかどうかをチェックする HTTP ミドルウェア
- [auth/Listeners/SendEmailVerificationNotification.php](https://github.com/illuminate/auth/blob/5a4e69270a2ac9a73ab2cf767b01bbcc1dd5c2fb/Listeners/SendEmailVerificationNotification.php)
  - ユーザの登録完了（`Registered`）イベントに呼応して `VerifyEmail` メールを送るイベントリスナー
  - デフォルトで [EventServiceProvider](https://github.com/laravel/laravel/blob/d694bc06ccbf66f4f8ce432c39b71deea6766135/app/Providers/EventServiceProvider.php#L17-L21) に登録されている

これらのソースを読み解く上での一番の問題は

**「デフォルトでメールに記載される URL の行き先ってどうなってるの？」**

という点。上記の `VerifyEmail` のソースを読むと…

```php
/**
 * Get the verification URL for the given notifiable.
 *
 * @param  mixed  $notifiable
 * @return string
 */
protected function verificationUrl($notifiable)
{
    if (static::$createUrlCallback) {
        return call_user_func(static::$createUrlCallback, $notifiable);
    }

    return URL::temporarySignedRoute(
        'verification.verify', // ←これなに？？？？
        Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
        [
            'id' => $notifiable->getKey(),
            'hash' => sha1($notifiable->getEmailForVerification()),
        ]
    );
}
```

突然 `verification.verify` という謎の指定が現れますが，これは他の UI を提供するためのパッケージで生成されるルートの名前です。

- [laravel/ui の実装](https://github.com/laravel/ui/blob/e19f10bedee289fa5e70807a5e2476888aaf7ad2/src/AuthRouteMethods.php#L92)
- [laravel/breeze の実装](https://github.com/laravel/breeze/blob/9f46ea7b9343b06d98552a12a913b38e92fb5974/stubs/default/routes/auth.php#L41-L43)
- [laravel/fortify の実装](https://github.com/laravel/fortify/blob/b9cb52ec4a00500a80bcd2839c9a228958e7d3d9/routes/routes.php#L87-L89)<br>（JetStream は Fortify を利用しているのでこれに含まれる）

暗黙の了解で飛び道具が出現するので，これはしっかりソースを読んでいないと追うのが大変…

### パスワードリセット

以下の記事で `PasswordBroker` という概念について詳しく解説されています。

https://zenn.dev/fagai/articles/d9188458766a7ecdfda9

このパッケージの文脈においては，「トークン」は **「パスワードリセットトークン」** を意味することに注意してください。つまり，言い方を変えれば， 

**<font color=red>`illuminate/auth` にはパスワードリセットトークン以外のトークンは出現しない</font>**

ということになります。また，パスワードリセットにおいても `password.reset` というルートの名前が現れます。

- [laravel/ui の実装](https://github.com/laravel/ui/blob/e19f10bedee289fa5e70807a5e2476888aaf7ad2/src/AuthRouteMethods.php#L65)
- [laravel/breeze の実装](https://github.com/laravel/breeze/blob/61ffbd4a4f2f0e055f0d2b0bf80926c7c904b2df/stubs/api/App/Providers/AuthServiceProvider.php#L28-L30)
  - なんでここだけオレオレやねん！ｗ ↓
  > ```php
  > ResetPassword::createUrlUsing(function ($notifiable, $token) {
  >     return config('app.frontend_url')."/password-reset/$token?email={$notifiable->getEmailForPasswordReset()}";
  > });
- [laravel/fortify の実装](https://github.com/laravel/fortify/blob/b9cb52ec4a00500a80bcd2839c9a228958e7d3d9/routes/routes.php#L53-L55)<br>（JetStream は Fortify を利用しているのでこれに含まれる）

### シンプルな認可

`Gate` と `Policy` を使った認可です。これについては，最初にも引用した以下の記事で詳しく解説しています。

https://qiita.com/mpyw/items/8c5413b99b8e299f7002

## `laravel/breeze`: 基本的な MPA 認証

Breeze, Fortify, JetStream などが，コアの `illuminate/auth` の機能を利用して実装している部分です。

### 最小限の認証ロジックに対するバックエンドの HTTP 層実装

既に「ルーティングで飛び道具が使われているよ」と述べた通りです。メールに記載される URL のルーティングだけについて触れましたが，メール送信よりも前のログイン画面などもこちらに含まれます。

### フロントエンド

歴史的なものは除外し，最新の Breeze についてのみ触れます。 Breeze は **[4 個のスタック](https://github.com/laravel/breeze/tree/61ffbd4a4f2f0e055f0d2b0bf80926c7c904b2df/stubs)** から実装を選ぶことができるようになっています。

- **Default**
  - 簡易的・最小限の機能を提供する実装。既に示している通り， `illuminate/auth` にはフロントエンドと HTTP ルーティング・コントローラなど末端部分は含まれていないので，それを実際に動作させるために必要になります。 CSS は Tailwind ベース。
- **API**
  - **<font color=red>`laravel/sanctum` を任意でインストールすることができ</font>**，その場合に限定して使われます。 Sanctum 自体についての説明は後述します。
- **Inertia-React<br>Inertia-Vue**
  - 「インテリア」っぽく空目しますが正しくは「イナーシャ」らしいです。**モノリスらしく React/Vue の面倒は全部 PHP で見ようや！という思想。<font color=red>ページロード時のバックエンドからフロントエンドへのデータの流し込みをシームレスに行う</font>ことにフォーカスしているライブラリです。** しかしフロントエンドはフロントエンドで独立して時代が進んでいくものなので，今更バックエンド側に全部含めようという思想はあまり時代にマッチしていないでしょう。バックエンドに完全にロックインしてもいい，プロトタイプや管理画面で割り切って使うぐらいにとどめておくのがよいと思います。

https://github.com/inertiajs

「MPA」と言いましたが，ここでいう MPA には **「Laravel で全て面倒を見る一部 SPA なアプリケーション」** が含まれます。<br>（Inertia でフル SPA を作ることは困難で，あくまで部分的なユースに限られると思われます）

## `laravel/fortify`: カスタマイズ性の高い拡張認証認可コアロジック

https://github.com/laravel/fortify

Fortify, JetStream でのみ利用できる機能です。 JetStream は Fortify を利用して構築されているので，共通の機能に関しては Fortify 視点で解説します。

### 細部の実装を自由に差し替えられるコントラクト

Fortify の特徴を掴むためには，以下のファイル群を見ると最もわかりやすいでしょう。

https://github.com/laravel/fortify/tree/246cf1d01b5481c71c8fe4609396159b7e06393f/src/Contracts

ずらーっとコントラクトばっかり並んでいますね。これら対応した **実装はすべてサービスコンテナ経由で作られる** …これが意味するのは **<font color=red>あらゆる実装を簡単にカスタムできる</font>** ことです！

### 拡張認証認可コアロジックに対するバックエンドの HTTP 層実装

アプリケーションの要件に依存する **<font color=red>フロントエンドのビューは全く含まれない</font>** パッケージですが，ビューを制御するレスポンスクラスである **`SimpleViewResponse`** ベースの実装は既にされているため， **パス通りにビューファイルを配置するだけでいい** ようになっています。

https://github.com/laravel/fortify/blob/246cf1d01b5481c71c8fe4609396159b7e06393f/src/Fortify.php#L90-L196

> ```php
> /**
>  * Register the views for Fortify using conventional names under the given prefix.
>  *
>  * @param  string  $prefix
>  * @return void
>  */
> public static function viewPrefix(string $prefix)
> {
>     static::loginView($prefix.'login');
>     static::twoFactorChallengeView($prefix.'two-factor-challenge');
>     static::registerView($prefix.'register');
>     static::requestPasswordResetLinkView($prefix.'forgot-password');
>     static::resetPasswordView($prefix.'reset-password');
>     static::verifyEmailView($prefix.'verify-email');
>     static::confirmPasswordView($prefix.'confirm-password');
> }
> ```


また，ビューを含まない**リダイレクトレスポンスはデフォルト実装そのままで使用できます**。

- [LoginResponse](https://github.com/laravel/fortify/blob/246cf1d01b5481c71c8fe4609396159b7e06393f/src/Http/Responses/LoginResponse.php)
- [VerifyEmailResponse](https://github.com/laravel/fortify/blob/246cf1d01b5481c71c8fe4609396159b7e06393f/src/Http/Responses/VerifyEmailResponse.php)
- …

また， ユーザ登録などに関する HTTP リクエストを受けて，その情報をデータベースに保存する部分など関して。アプリケーションによってユーザ情報の持ち方は様々であり，最も自由度が求められる部分です。ここをカスタマイズしやすくするために，一部のファイルはコマンドによって <font color=red>**書き換える前提でスタブから生成されるようになっています**</font>。

https://github.com/laravel/fortify/tree/1.x/stubs

これらのスタブを自分のアプリケーションに合うように編集し，ビューファイルを設置するところまでが完全にアプリケーション開発者に委譲されています。

### 使用する機能の選択

https://github.com/laravel/fortify/blob/7da6504f5f8a5fe6854dedaffc81ac497194ba56/stubs/fortify.php#L135-L144

以下の設定ファイルから分かる通り，使う機能のみを有効化することができる仕組みがあります。

> ```php
> /*
> |--------------------------------------------------------------------------
> | Features
> |--------------------------------------------------------------------------
> |
> | Some of the Fortify features are optional. You may disable the features
> | by removing them from this array. You're free to only remove some of
> | these features or you can even remove all of these if you need to.
> |
> */
> 
> 'features' => [
>     Features::registration(),
>     Features::resetPasswords(),
>     // Features::emailVerification(),
>     Features::updateProfileInformation(),
>     Features::updatePasswords(),
>     Features::twoFactorAuthentication([
>         'confirm' => true,
>         'confirmPassword' => true,
>         // 'window' => 0,
>     ]),
> ],
> ```

- ユーザ登録
- パスワードリセット
- メールアドレス検証
- ユーザ情報変更
  - 「プロフィール」とあるので何やらリッチな情報がありそうですが，スタブで提供される実装は **「名前」「メールアドレス」** だけ です
- パスワード変更
- 多要素認証

### MFA (Multi-Factor Authentication, 2-Factor Authentication)

<del>何が二要素認証じゃ多要素認証と言え</del>

Fortify を採用する最も大きなメリットの 1 つです。今どき自前で認証を吸えるなら， MFA の実装はセキュリティ上必須に近いと言ってよいでしょう。

- [TwoFactorAuthenticationProvider](https://github.com/laravel/fortify/blob/246cf1d01b5481c71c8fe4609396159b7e06393f/src/TwoFactorAuthenticationProvider.php)
- **[TwoFactorAuthenticatable](https://github.com/laravel/fortify/blob/246cf1d01b5481c71c8fe4609396159b7e06393f/src/TwoFactorAuthenticatable.php)**
  - ここがロジックのコアですが，  Eloquent Model にミックスインするトレイトとなっているので，めちゃくちゃデータベースと密結合になります。<del>ぶっちゃけ設計めっちゃ汚いです</del>。このあたりが気になる場合は， Fortify に依存せずに自前で実装してみてもよいでしょう。<del>中身ライブラリ丸投げでペラッペラだし全然自前で書けるやろこのぐらい</del>
- [RecoveryCode](https://github.com/laravel/fortify/blob/246cf1d01b5481c71c8fe4609396159b7e06393f/src/RecoveryCode.php)

<font color=silver>Fortify 最高！…と言いたいのですが，更に実装にちょっとナンセンスなところがあり，それ起因で私のライブラリに不具合報告が来ている点がちょっと不満です…一応修正 PR を出したのですがマージされるかどうか…</font>

https://github.com/laravel/fortify/pull/393

**追記: マージされませんでした。<del>いつもの定型文で</del>詳細不明のお断りをされました**

### スロットリング

`LoginRateLimiter` というクラスによる処理がログインに絡む随所に仕込まれており，攻撃を受けた場合に速めに検知してロックアウトさせることができます。**「メールアドレス」「IPアドレス」の組み合わせがキーになります。**

https://github.com/laravel/fortify/blob/2e084ad87ec52db136314f83ba4a1e45ce8ce311/src/LoginRateLimiter.php#L92

<del>これも `RateLimiter` の薄いラッパークラスなので Fortify 独自機能かというと微妙なところよね</del>

このあたりを鑑みると， Foritfy の一番の恩恵はあらゆる実装を差し替え可能なことであり， MFA やスロットリングはおまけ機能と考えてよいでしょう。

## `laravel/jetstream`: Fortify ベースの全部入りパッケージ

https://github.com/laravel/jetstream

Fortify では抽象化されていた，フロントエンドのビューも何から何まで全部付いてくるパッケージです。

機能が盛り沢山ですが，<del>個人的に使う気が起きないので</del>細かい説明は割愛します。基本は Fortify をカスタムして使うのが一番筋がいいと思う。

### RBAC (ロールベースアクセス制御)

JetStream 固有の実装の 1 つがこれ。

https://github.com/laravel/jetstream/tree/efda2a4859a203f5f90d076eb127515a19c9b3b5/src

- [Team.php](https://github.com/laravel/jetstream/blob/efda2a4859a203f5f90d076eb127515a19c9b3b5/src/Team.php)
- [Role.php](https://github.com/laravel/jetstream/blob/efda2a4859a203f5f90d076eb127515a19c9b3b5/src/Role.php)
- [HasTeams.php](https://github.com/laravel/jetstream/blob/efda2a4859a203f5f90d076eb127515a19c9b3b5/src/HasTeams.php)
- [HasProfilePhoto.php](https://github.com/laravel/jetstream/blob/efda2a4859a203f5f90d076eb127515a19c9b3b5/src/HasProfilePhoto.php)
- [database/migrations/2020_05_21_200000_create_team_user_table.php](https://github.com/laravel/jetstream/blob/efda2a4859a203f5f90d076eb127515a19c9b3b5/database/migrations/2020_05_21_200000_create_team_user_table.php)

この並びを見たら何となく実装の想像が付く人も多いんじゃないでしょうか？ユーザがチームに所属して，チームに所属するときにロールを持って…（なんか知らんけどプロフィール写真の機能もある…）

ただ，このテーブル構造だけで，実際の商業レベルで要求される RBAC をすべて満たすことはほぼ不可能だと思います。「Breeze よりリッチな機能を持ったお遊びパッケージ」という言葉がよく似合うかと。

### Livewire フロントエンド

Breeze のときはフロントエンドは Inertia というライブラリ一択でしたが， JetStream では Livewire というものも選択できるようです。

https://laravel-livewire.com/

ドメインに Laravel が入っている通り，  **Inertia を超えるレベルで更に Laravel とフロントエンドが密結合になるライブラリ** です。その代わり， Inertia では不可能だった **<font color=red>双方向データバインディング</font>** を実現できる可能性を秘めています。

「全部 PHP で SPA を作れたらいいのに〜」という PHPer の夢が詰まっています。裏でめっちゃ頑張ってリアルタイム通信しているようです。<del>超絶ブラックボックスなので使用は慎重に</del>

## `laravel/sanctum`: SPA 認証 ＆ ネイティブアプリ認証

https://github.com/laravel/sanctum

`laravel/passport` は **OAuth 2** プロトコルに準拠したサーバを提供できますが，実装が複雑すぎて敬遠されることが多かったようです。現在は， トークンを発行する用途では Passport を簡略化した `laravel/sanctum` が使われることが一般的です。

ところで Sanctum は，以下のように使われることを想定しています。

| 方式 | クライアント |
|:---|:---|
| 伝統的 Cookie ベースのセッション認証 | **自社の SPA のための API** |
| トークン認証 | ネイティブアプリ<br>他社提供する API |

「SPA といえばトークンでしょ」という時代もありましたが， **<font color=red>トークンを Web ブラウザ上の LocalStorage 配下で管理させるのはトークン漏洩リスクが高く</font>，敬遠されるべき** と言われるようになってきました。

https://mizumotok.hatenablog.jp/entry/2021/08/04/114431

上記の記事ではインメモリが推奨されていますが，トークン漏洩耐性がフロントエンドの実装に左右されるため，フルマネージドな Auth0 などに乗っからない場合はサーバ側で完結できていたほうが無難，という考え方もできるでしょう。一方， JavaScript から中身を覗くことができない **HttpOnly** な Cookie であったとしても CSRF をケアしなければならないという話は， （上記の記事では触れられていませんが） **<font color=red>SameSite=Lax</font>** という設定によって大幅にリスクを減らすことができるようになりました。

https://laboradian.com/same-site-cookies/

ただ <font color=red>SameSite=Lax では GET リクエストに関しての CSRF は防げません</font>。 API アクセス前提であれば GET リクエストにも **Origin ヘッダ検証** や **CSRF トークン検証** も加えれば完璧ということになりますが… Sanctum はまさにその方法で安全性を担保しています。個人的な意見としては，  **HttpOnly + SameSite=Lax を両方有効にした Cookie に CSRF トークンを組み合わせる** が，自社 SPA の API を提供する上ではバランスが最も取れている選択肢だと感じています。

https://qiita.com/mpyw/items/0595f07736cfa5b1f50c

:::note warn
HttpOnly の指定によって XSS それ自体の対策として万全になるわけではありません。あくまで **「XSS されても認証情報の直接的な流出は避けられる」** だけ，即ち自分が Web サイトを閲覧していないときにも好き勝手されるという状態を回避できるだけであって，**<font color=red>そもそも XSS 脆弱性を生んでしまった時点で致命的です</font>**。これを踏まえてもなおメリットとして成立する点としては，以下のようなものが挙げられます。

- パスワード変更・メールアドレス変更・アカウント削除・API キーの発行など， **アカウントの操作に直結する重要なアクションに 「現在のパスワードの再確認」を挟んでいれば** ，ログイン権限の奪取やバックドアに対しては耐性を持つ。
- XSS が確認された後に，認証情報を直ちに無効化しなくても被害が拡大しにくい。 XSS 脆弱性を潰せばひとまず時間的猶予はできる。もし認証情報そのものが流出してしまうと，それの無効化など，インシデント対応の手間が増える。
:::

https://www.youtube.com/watch?v=4JREwhSC2dQ

### 自社 SPA 向けの Cookie 認証

Sanctum の自社 SPA 向け実装の意図を理解するためには，以下の 3 つのファイルを読むといいです。

- **[Guard.php](https://github.com/laravel/sanctum/blob/863b610662bc09091fb73188f127d65eb64d3398/src/Guard.php#L56-L62
)**
  - `__invoke()` の前半部分が全てです。この処理は `illuminate/auth` の `RequestGuard` から呼ばれる部分ですが， 後半のトークン認証に分岐している部分を除けば，ほぼ `SessionGuard` に処理を丸投げしているだけです。つまり， **トークン認証を使わないのであれば直接 `SessionGuard` を使うのと大差ありません**。
  ```php
  /**
   * Retrieve the authenticated user for the incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return mixed
   */
  public function __invoke(Request $request)
  {
      foreach (Arr::wrap(config('sanctum.guard', 'web')) as $guard) {
          if ($user = $this->auth->guard($guard)->user()) {
              return $this->supportsTokens($user)
                  ? $user->withAccessToken(new TransientToken)
                  : $user;
          }
      }
      // ...
  }
  ```
  - トークン認証にも対応したユーザを使っている場合，  Cookie 認証ではダミーの `TransientToken` が格納されます。
- **[Http/Middleware/EnsureFrontendRequestsAreStateful.php](https://github.com/laravel/sanctum/blob/863b610662bc09091fb73188f127d65eb64d3398/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php)**
  - HTTP ミドルウェアです。上で説明したように，理想的なセキュリティ検証を行っていることが一目で分かると思います。認証用の Cookie は `HttpOnly` かつ `SameSite=Lax` です。
- **[Http/Controllers/CsrfCookieController.php](https://github.com/laravel/sanctum/blob/2.x/src/Http/Controllers/CsrfCookieController.php)**
  - これ自体は何もしないエンドポイントですが，ここ（`/csrf-cookie`）にアクセスしてきたときに，他のリクエストを送るとき必要な `X-XSRF-TOKEN` というヘッダで付与させる CSRF トークンを返すことを意図しています。
  - [VerifyCsrfToken.php](https://github.com/laravel/framework/blob/4d46bda6f19d490080512fa053065081f737bf2f/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php#L212) と [Cookie.php](https://github.com/symfony/http-foundation/blob/86119d294e51afe4d8e07da96b63332bd1f3f52c/Cookie.php#L84) を見ると分かる通り， **<font color=red>CSRF トークン用の Cookie は `HttpOnly` ではありません</font>**。これは JavaScript から読み取って使われる想定であるためです。<br>（万が一 XSS で盗まれても直ちに乗っ取りには繋がりません）

### ネイティブアプリ/サードパーティ向けのトークン認証

ネイティブアプリ向けにはトークンを使うことが極めて一般的であり，特に懸念はありません。同じように，ソースコードからアプローチしてみましょう。

- **[Guard.php](https://github.com/laravel/sanctum/blob/863b610662bc09091fb73188f127d65eb64d3398/src/Guard.php#L64-L92
)**
  - `__invoke()` の後半部分が該当します。トークン専用のテーブルがあり，有効性検証後にトークンに紐付くユーザを取得しているのが分かります。トークンを最後に使用した日時が記録されるのは分かりやすくていいですね。<del>RDB 上でこれをやるのは書き込みコストはどうなんだろう</del>
  ```php
  /**
   * Retrieve the authenticated user for the incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @return mixed
   */
  public function __invoke(Request $request)
  {
      // ...
 
      if ($token = $this->getTokenFromRequest($request)) {
          $model = Sanctum::$personalAccessTokenModel;
  
          $accessToken = $model::findToken($token);
  
          if (! $this->isValidAccessToken($accessToken) ||
              ! $this->supportsTokens($accessToken->tokenable)) {
              return;
          }
  
          $tokenable = $accessToken->tokenable->withAccessToken(
              $accessToken
          );
  
          event(new TokenAuthenticated($accessToken));
  
          if (method_exists($accessToken->getConnection(), 'hasModifiedRecords') &&
              method_exists($accessToken->getConnection(), 'setRecordModificationState')) {
              tap($accessToken->getConnection()->hasModifiedRecords(), function ($hasModifiedRecords) use ($accessToken) {
                  $accessToken->forceFill(['last_used_at' => now()])->save();
  
                  $accessToken->getConnection()->setRecordModificationState($hasModifiedRecords);
              });
          } else {
              $accessToken->forceFill(['last_used_at' => now()])->save();
          }
  
          return $tokenable;
      }
  }
  ```
- **[CheckAbilities.php](https://github.com/laravel/sanctum/blob/863b610662bc09091fb73188f127d65eb64d3398/src/Http/Middleware/CheckAbilities.php)**
  - トークンの権限をチェックするミドルウェアです。エンドポイントごとに設定する使い方が想定されています。 
- **[HasApiTokens.php](https://github.com/laravel/sanctum/blob/404f1f4b603d8d3b2a0e35620b9c60c246ea5134/src/HasApiTokens.php#L37-L53)**
  - `User` モデルにミックスインして使うトレイトです。トークンを作成するときに **`createToken()`** を呼び出します。**デフォルトではアビリティは `*` になっていて，全てのアクションが許可されます**。
  ````php
  /**
   * Create a new personal access token for the user.
   *
   * @param  string  $name
   * @param  array  $abilities
   * @return \Laravel\Sanctum\NewAccessToken
   */
  public function createToken(string $name, array $abilities = ['*'])
  {
      $token = $this->tokens()->create([
          'name' => $name,
          'token' => hash('sha256', $plainTextToken = Str::random(40)),
          'abilities' => $abilities,
      ]);
  
      return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken);
  }
  ````

以上です。<del>若干ソースコードクオリティが気になる部分はありますが</del>， Passport と比べると Sanctum はかなりシンプルで，本当に必要なものだけを残した感がありますね。

## `laravel/socialite`: OAuth 1, OAuth 2 プロトコルを用いたソーシャルログインのためのクライアント

https://github.com/laravel/socialite

これまでは `illuminate/auth` に関連したものでしたが，これは全く用途が異なります。これは**サードパーティのサービスにユーザを OAuth でログインさせて，ユーザの代わりになってそのサービスの API を叩けるようにする機能**です。実際に「Sign in with Twitter」などの機能を実現する場合， Twitter でログインして取得した情報を，自分のアプリケーションの管理下にあるテーブルに保存する必要があります。この部分の面倒は見てくれないので，自分で実装しましょう。

https://qiita.com/ucan-lab/items/685290df8faa498df5bf

# 選定基準

既に @localdisk さんの記事でも触れられているとおりですが，概ね以下のようなところでしょう。

まず，用途が明確なものを整理します。

- <ins>ソーシャルログイン</ins>を実装したい場合， とりあえず **Socialite** は入れる。但し，これだけで自分のアプリケーション側の認証管理はできないので，他と組み合わせることになる。
- <ins>自社 SPA 向けの API 認証</ins>や，<ins>ネイティブアプリ・サードパーティ向けのトークン認証</ins>を検討する場合， とりあえず **Sanctum** は入れる。但し，（コアの `illuminate/auth` を含む）他と組み合わせることになる。
  - サードパーティ提供を重視し，<ins>厳格な OAuth 2 プロトコル</ins>に則ったサーバを立てたい場合， Sanctum の代わりに **Passport** を検討する。但し， Sanctum のように 「１つ立てておけば Cookie もトークンも対応できる」 という特性はなく， Guard を 2 つ並べることになるだろう。

次に， 基盤や MPA 向けの機能を提供するものを整理します。

- Auth0 や Cognito などの<ins>フルマネージドサービスや自社の認証基盤</ins>に乗っかる場合は，  **`illuminate/auth`** をそのまま使うのがベスト。また， Sanctum が提供する <ins>SPA やネイティブアプリ向けの認証で全て完結</ins>する場合もこれで OK。<br><br>以下，<ins>**自前でガッツリユーザ情報を管理する**</ins>場合。<br><br>
- 初心者の<ins>ポートフォリオ制作</ins>などでは **Breeze** が最適。
- 一般的な<ins>商用ユースケース</ins>には **Fortify** が最適。
- **JetStream** は積極的に使うべきではないが，「<ins>管理画面用途であるため UI デザインは関心がない</ins>」かつ「<ins>Breeze では機能不足</ins>」というときには有用。