Laravel API認証 vs Passport vs Sanctum 違い

とあるSPA開発をしていて、APIとしてlaravelを使うことにしておりユーザー認証はOAuth2つまりLaravel Passportを使っていて実は２年前にLaravel6.xで実装済。

認証の部分は済ませていて後は各画面だったり機能の開発に専念できる状態にまでしていたのだが、知り合いから頼まれてたような開発なのでプライベート時間を使ってちょこちょこやってはいたんだけど、ちょっと時間が空き過ぎて見返しても思い出せん部分がちらほら。

そもそもSPAの認証の仕組みってどうなってるんだっけって状態にまで...。
オイラも２年前から多少は成長しているわけでAPI側見てみてもデザパタがなってなかったりいづれにしてもそこそこのリファクタが必要だったりする。フロント側もatmic手法でコンポーネント切り分けできてないな...って今ではそういう目線で見たりしてる。

とは言え一人で作ったやつだからリファクタするにしてもそこまで大掛かりではない。
なのでもう最初から作り直すことにした。

ちなみにフロントはnuxt.js/vuetifyでAPIはLaravel6.xである。
あと2ヶ月もしたらlaravelLTS9.xがでるから、今最初から作り直すのはもどかしい気持ち。

## routes/api.phpのauth:apiってなに？使い道は？

もうこんなレベルに落ちるまでリア充しておりました、はい。

```php
<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

```

## 答え

* userテーブルにapi_tokenカラムを持たせる

```php
$table->string('api_token', 80)->after('password')
                ->unique()
                ->nullable()
                ->default(null)->comment('API トークン');
```

* 以下でtokenを作り、適当なユーザーデータを作る

```php
dd(\Str::random(60))
```

* route定義はgetなのでとりあえずブラウザで以下を実行してみる

https://local.api.xxx.jp/api/user

* まぁこんなエラーがでるが

> Symfony\Component\Routing\Exception\RouteNotFoundException
Route [login] not defined.

* `app/Http/Middleware/Authenticate.php`の処理の結果である

```php
class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string|null
     */
    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {
            return route('login');
        }
    }
}
```

* で、今度はクエパラを付与してアクセスしてみる

https://local.api.xxx.jp/api/user?api_token=jD5SB9NpzyQjhHGgxWXcYgUkxkB74rrfXL6Z67b4aQH9cdMVcFPx3QDvKh46

合致するapi_tokenのユーザーでログインしたことになる

> {"id":1,"name":"test","email":"test@t1e2s3t4.co.jp","email_verified_at":null,"api_token":"jD5SB9NpzyQjhHGgxWXcYgUkxkB74rrfXL6Z67b4aQH9cdMVcFPx3QDvKh46","created_at":null,"updated_at":null}

詳細知りたいなら以下を。

https://readouble.com/laravel/6.x/ja/api-authentication.html

## じゃあlaravel passportとの違いは？ちなみにSanctumってどうなのよ？

SanctumはLaravel8.xから導入された比較的軽量な認証のエコシステムらしい。
システムがOAuth2に準拠するのならpassport使えとある。

https://stackoverflow.com/questions/60812883/laravel-sanctum-vs-passport

> Passportは、Laravelアプリケーションに完全なOAuth2サーバー実装を数分で提供します。したがって、OAuth2についての簡単な知識が必要です。
Sanctumは、OAuthを複雑にすることなく、ユーザーにAPIトークンを発行するためのシンプルなパッケージです。Sanctumは、Laravelの組み込みのCookieベースのセッション認証サービスを使用します。
小さなアプリケーションでは、Sanctumを使用します。シンプルで簡単です。

小さなアプリケーションと言われてもなぁ...もっと具体例がないと判断材料にできん。
だから正直どういうケースのときにpassportとsanctumを使うべきなのかの説明はできん。

ただ説明を見る限りは、SanctumにできることはPassportもできるけど、PassportにできてSanctumにはできないこともあるみたいだから個人的にはpassport使っとけばいいと思う。

エンジニアらしい回答でなくてすまんな、エンジニア上がりの経営者なので後々リプレイス案件にでもすればいいと思っている。まずは稼ぐことが大事なのだ。

そしたら次は、`API認証 vs Passport` だ。

## API認証 vs Passport

auth:apiの方は認証できなければログインページのリダイレクト処理が入ってる。
Passportは認証できなければjsonでresしてる。

まぁどっちを使っても、例えばauth:api使ってもredirectToでjsonをreturnすればPassportと同じようなことはできると思ってるが、流石にそこは気持ち悪さがある。redirectToじゃないし的な。

どちらかと言えばフロントがjsonを受け取ってその結果を、vue-rooterの対象にリダイレクトさせるようにすべきだろう。

で、話を戻してとある記事ではこんな風に書かれている。


> Laravelの外側のアプリケーションからアクセス制限のかかっているLaravel内のリソースにアクセスするためにはどうしたらいいのでしょう。そんな疑問を解決してくれるのがLaravel Passportです。


そして公式にはこんな風にも書かれている。

> Note: Laravelでは、シンプルなトークンベースの認証ガードを提供していますが、API認証を提供する堅牢なプロダクションアプリケーションでは、Laravel Passportの使用を考慮することを強く推奨します。

`API認証を提供する堅牢なプロダクションアプリケーション`とは。具体例はよ。

別にauth:apiでも外部からapi-token付与してアクセスしたらユーザー情報は引っ張って来られる。
だからpassportとの違いがわからない。

とりあえず堅牢にすべきかそうでないかと問われたら堅牢にしたくないなんてプロダクトオーナーはいないだろう。
よってpassportを使っとけばいいんでねーかというのが結論。

https://readouble.com/laravel/6.x/ja/passport.html

明確に使い分けを説明できる勇者様が現れましたら記事は更新します。

## 追記

↓堅牢です、はい。

`{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiMjVjZjllNDk5Njc2ZmQ1ODdiYTg2MGMwMGNlNTE3NmIyYTY2NmE3ZDhmZmI3NTdhMWM2MDE0MmUzZjZmMDQxOTRmMTdiMWM0MDczNTliOTciLCJpYXQiOjE2MjQyOTAwNjksIm5iZiI6MTYyNDI5MDA2OSwiZXhwIjoxNjU1ODI2MDY5LCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.H8tIfRCDkQJR6QQktcxU0Gap34LtKdHSstToAMnsNEtAgteEe-3I9NteBmysmJgxshWkagerDdIwQBtgZvQRa06O8a-_daB-oDIbRgob270eee7j2YW6yQqklSuZSSP-0zqfkOBlRSCuzoVHBRYE1kGs_ytIhxVeJF0tdWHkCsp6tqOE8-nzCycgwhA0yob6xMHHLio_AhKlXvsplHKPQO6o1v-MinF4C_fiqxQd-6ksFCVoifQkoiUzVF3GGtEem5mWYTNsZ0yAovEXk7ca2qJNaTpNFY1tBupfqLCesWhE_x2PYJnAhU37_8yZIsQn45tgUGK7Oi_iSZhUlXn2_eVK3wm_R04EJQI-3moRPiFaKbk3vaaz7sQVM74t6Zy_SX4Z-B-xgBrVgO9CLQ6qUnC8nWE5l0pRFqQGGa5XBkHt8nELipFRjyEZ4sV6NXdjDUE-uJmYYef_WqHWbKtx0YOYyMSfrgoiHDG9leEQwcYWAXZjtUMR_Df-5n7dQVvqX3oK-z1uBWlTJRMwvYyE_ou4TzJqFvaOIgOdb1fnFhEUKS8tOPlJAC4fj4o2fLpxrCGj1DLzFogHzCynqO_qCXVB76pIG9snxotUoVsbhYN8YiNCHyH4N0g28Cw4GXmBOpxk7Aax0c0qWmmz_9d6zAKvjzZKztUotPdcJtKuo9A"}`