GraphQL(lighthouse)+jwt認証 in laravel6

## 概要
laravel6での動作確認をそれぞれしつつGraphQLでjwt認証を実装してみました。
備忘録。

## 環境
- laravel 6.3.0
- lighthouse 4.5
- jwt-auth 

## インストール
### lighthouse

```
composer require nuwave/lighthouse
```

### laravel-graphql-playground
開発に便利なplaygroundもいれる

```bash=
composer require mll-lab/laravel-graphql-playground
```

### jwt-auth

```bash=
composer require tymon/jwt-auth ^1.0.0
```

`^1.0.0`じゃないと入ってくれない
ドキュメント要チェック

### 入れた結果
composer.json

```json
    "mll-lab/laravel-graphql-playground": "^1.1",
    "nuwave/lighthouse": "^4.5",
    "tymon/jwt-auth": "^1.0.0"
```

## lighthouseとgraphql-playgroundの初期設定
### lighthouse

参照: https://lighthouse-php.com/4.5/getting-started/installation.html

- 設定ファイルを作成

```bash=
# providerの場所が古いバージョンとは変わっているので注意　ドキュメントを参考に
# config/lighthouse.php
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider"
```
### laravel-graphql-playground

- 設定ファイルとviewを作成

```bash=
# config/graphql-playground.php
php artisan vendor:publish --provider="MLL\GraphQLPlayground\GraphQLPlaygroundServiceProvider"
```
`/graphql-playground`にアクセスすると実行画面が表示される
<img width="1440" alt="playground.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489139/d6a74df0-1ff6-fd37-5158-5d5fea0d3624.png">


## jwt-authの初期設定

- 設定ファイル

```bash=
# config/jwt.php
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
```

- 秘密鍵の作成

```bash=
# .envに追加される
php artisan jwt:secret
```

## jwt-authで認証するModelを作成
API認証でjwtを使うために、`JWTSubject`を認証するモデルに実装する。
今回はデフォルトのUserモデルを認証に使う。
implementsして`getJWTIdentifier`と`getJWTCustomClaims`の２つのメソッドを定義

```php
<?php

namespace App;

// ~略~
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
   // ~略~
   
    // add
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }
    
    // add
    public function getJWTCustomClaims()
    {
        return [];
    }

}
```

- `getJWTIdentifier`
jwtトークンを取得する

- `getJWTCustomClaims`
jwtトークンにClaim（情報）を追加したい時にはここに追記する

```php
// 例
public function getJWTCustomClaims()
{
    return [
        'email' => $this->email
    ];
}
```

## config/auth.phpの修正
Guardの設定を変更

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

    'api' => [
        'driver' => 'jwt', // add
        'provider' => 'users',
        // 'hash' => false, つかわない
    ],
],
```

## ログインするクエリを作成

```bash=
php artisan lighthouse:query Login
```

`app/GraphQL/Queries`に作成されます。

認証ロジックはあっさりとこんな感じにしてみました。
`$args`にパラメータが入ってきます。
成功したらaccess_tokenを返す。

```php
<?php

namespace App\GraphQL\Queries;

use GraphQL\Type\Definition\ResolveInfo;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;

class Login
{
    public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        if (!$token = auth('api')->attempt(['email' => $args['email'], 'password' => $args['password']])) {
            return ['error' => 'Unauthorized'];
        }
        return ['access_token' => $token];
    }
}

```

## schemaに定義

`graphql/schema.graphql`に記述する。
デフォルトで色々書いてありますが、それは置いておいて以下のように定義してみる。
`@middleware`ディレクティブでミドルウェアを定義することが出来る。
ログインなので`guest:api`ミドルウェアを適用。

```graphQL
type Query {
    login(email: String! password: String!): LoginData @middleware(checks: ["guest:api"])
}

# 返ってくるデータのスキーマ
type LoginData {
    access_token: String
    error: String
}
```

## ログインする
### クエリを叩く
`/graphql-playground`にて

```
{
  login(email: "test@example.org" password: "password") {
    access_token
    error
  }
}
```
実行

### 結果

成功

```
{
  "data": {
    "login": {
      "access_token": "ここにトークン",
      "error": null
    }
  }
}
```

失敗

```
{
  "data": {
    "login": {
      "access_token": null,
      "error": "Unauthorized"
    }
  }
}
```

### 認可ルートを設定
`@middleware`ディレクティブを使用して`auth:api`ルートを定義

```GraphQL
type Query {
    login(email: String! password: String!): LoginData @middleware(checks: ["guest:api"])
    users: [User!]! @all @middleware(checks: ["auth:api"])
}
```

HTTP HEADERSでアクセストークンを渡す

```
{
    "Authorization": "Bearer アクセストークン"
}
```

結果
<img width="1439" alt="g.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489139/90f124dc-d1cb-cbf0-4287-d43888d0c353.png">


### おわり
`schema.graphql`を変更したら叩く

```bash=
php artisan lighthouse:clear-cache
```