Laravel PassportでOAuth2を用いた認証機能を作成しよう！④

# はじめに

本記事は Laravel Passport で OAuth2 を用いた認証機能を作成する手順の一部です。
事前にこちらの記事からご覧ください。

https://qiita.com/falya128/items/652a0d150faaaeb58b2b

※ 太字の部分が本記事で説明している内容です。

【リソースサーバ 兼 認証サーバ】
    [1. Laravel Passport のインストール](https://qiita.com/falya128/items/3b306194a5bad14ab582)
    [2. Laravel Passport の初期設定](https://qiita.com/falya128/items/3b306194a5bad14ab582)
    [3. ログイン画面の作成](https://qiita.com/falya128/items/3b306194a5bad14ab582)
    [4. マイページ画面の作成](https://qiita.com/falya128/items/3b306194a5bad14ab582)
    [5. ユーザ情報確認／更新画面の作成](https://qiita.com/falya128/items/ef8885b4fd902a05d8bc)
    [6. ログアウト処理の追加](https://qiita.com/falya128/items/ef8885b4fd902a05d8bc)
 
【クライアントアプリ】
    [7. トークンのリクエスト処理の追加](https://qiita.com/falya128/items/651daf3f4dd91ac70dcc)
    [8. Laravel Sanctum の初期設定](https://qiita.com/falya128/items/651daf3f4dd91ac70dcc)
    [9. ログインユーザ情報表示画面の作成](https://qiita.com/falya128/items/651daf3f4dd91ac70dcc)
    [10. ログアウト処理の追加](https://qiita.com/falya128/items/651daf3f4dd91ac70dcc)

【リソースサーバ 兼 認証サーバ】
    **11. Laravel Passport のルート登録**
    **12. トークン取消 API の作成**

# 作成手順

本記事ではリソースサーバ 兼 認証サーバを完成させて、最終的な動作確認まで行っていきます。

## 11. Laravel Passport のルート登録

Laravel Passport のデフォルトで登録されるルートを無視する設定を行っているため、本手順で必要なルートを登録します。

```diff_php:routes\web.php
Route::get('/logout', [App\Http\Controllers\AuthController::class, 'logout']);

+ Route::group([
+   'as' => 'passport.',
+   'prefix' => config('passport.path', 'oauth'),
+   'namespace' => '\Laravel\Passport\Http\Controllers',
+ ], function () {
+   Route::get('/authorize', [
+       'uses' => 'AuthorizationController@authorize',
+       'as' => 'authorizations.authorize',
+       'middleware' => 'web',
+   ]);
+   Route::post('/token', [
+       'uses' => 'AccessTokenController@issueToken',
+       'as' => 'token',
+       'middleware' => 'throttle',
+   ]);
+ });
```

また、`/oauth/token` ルートに関しては POST でのリクエストとなるため、`VerifyCsrfToken` ミドルウェアのチェックに引っかかってしまいます。そこで特例として `/oauth/token` だけは除外するように追記します。

```diff_php:app\Http\Middleware\VerifyCsrfToken.php
protected $except = [
-   //
+   '/oauth/token',
];
```

## 12. トークン取消 API の作成

`AuthController` にトークン取消処理を追加します。処理内容は以下の通りです。

1. `oauth_access_tokens` テーブルから `user_id` と `client_id` が一致する使用可能なトークンの `id` を取得する。
2. `id` が一致するアクセストークンを使用不可に更新する。
3. `id` が一致するリフレッシュトークンを使用不可に更新する。

```diff_php:app\Http\Controllers\AuthController.php
+   public function revokeToken(Request $request, string $clientId): JsonResponse
+   {
+       $tokenIds = Passport::token()
+           ->where('user_id', Auth::id())
+           ->where('client_id', $clientId)
+           ->where('revoked', false)
+           ->get()
+           ->pluck('id');
+
+       Passport::token()
+           ->whereIn('id', $tokenIds)
+           ->update(['revoked' => true]);
+
+       Passport::refreshToken()
+           ->whereIn('access_token_id', $tokenIds)
+           ->update(['revoked' => true]);
+
+       return response()->json();
+   }
```

作成したトークン取消処理にアクセスするルートを登録します。

```diff_php:routes\api.php
- Route::middleware('auth:api')->get('/user', function (Request $request) {
-   return $request->user();
+ Route::middleware('auth:api')->group(function () {
+   Route::get('/user', fn (Request $request) => $request->user());
+   Route::delete('/token/{clientId}', [\App\Http\Controllers\AuthController::class, 'revokeToken']);
});
```

# 動作確認

すべての手順が完了したので、実際に Authorization Code Grant を用いた認証を試してみます。

まずは、各プロジェクトで以下のコマンドを実行して、開発用サーバを起動します。

## リソースサーバ 兼 認証サーバ

```bash
npm run build
php artisan serve --port=8080
```

## クライアントアプリ

```bash
npm run build
php artisan serve
```

サーバを起動したら、`http://localhost:8000` にアクセスします。アクセスすると未認証の状態でログインユーザ確認画面が表示されるので、「ログイン」をクリックします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/c59e8c1c-3fea-f163-01a0-6310934d3ed6.png" width="400" />

クリックすると以下の順序でリダイレクトが行われ、ログイン画面が表示されます。

`http://localhost:8000/auth/redirect`
　→　`http://localhost:8080/oauth/authorize`
　→　`http://localhost:8080/login`

メールアドレスとパスワードを入力して「ログインする」をクリックすると以下の順序でリダイレクトが行われ、認証済みの状態で最初に表示したログインユーザ確認画面が表示されます。

`http://localhost:8080/login`
　→　`http://localhost:8080/oauth/authorize`
　→　`http://localhost:8000/auth/callback`
　→　`http://localhost:8000`

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339770/34c7c41e-336d-098a-683d-ae1b1383a0bb.png" width="400" />

ログイン処理と同様に「ログアウト」をクリックすると以下の順序でリダイレクトが行われ、未認証の状態でログインユーザ確認画面が表示されます。

`http://localhost:8000/logout`
　→　`http://localhost:8080/logout`
　→　`http://localhost:8000`


以上で動作確認は完了です。
リソースサーバ 兼 認証サーバに登録されたユーザを用いて、クライアントアプリで認証を行うことができました。