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もいれる

composer require mll-lab/laravel-graphql-playground

jwt-auth

composer require tymon/jwt-auth ^1.0.0

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

入れた結果

composer.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

  • 設定ファイルを作成
# providerの場所が古いバージョンとは変わっているので注意 ドキュメントを参考に
# config/lighthouse.php
php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider"

laravel-graphql-playground

  • 設定ファイルとviewを作成
# config/graphql-playground.php
php artisan vendor:publish --provider="MLL\GraphQLPlayground\GraphQLPlaygroundServiceProvider"

/graphql-playgroundにアクセスすると実行画面が表示される

jwt-authの初期設定

  • 設定ファイル
# config/jwt.php
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
  • 秘密鍵の作成
# .envに追加される
php artisan jwt:secret

jwt-authで認証するModelを作成

API認証でjwtを使うために、JWTSubjectを認証するモデルに実装する。
今回はデフォルトのUserモデルを認証に使う。
implementsしてgetJWTIdentifiergetJWTCustomClaimsの2つのメソッドを定義

<?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(情報)を追加したい時にはここに追記する

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

config/auth.phpの修正

Guardの設定を変更

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

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

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

php artisan lighthouse:query Login

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

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

<?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ミドルウェアを適用。

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

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

ログインする

クエリを叩く

/graphql-playgroundにて

{
  login(email: "[email protected]" password: "password") {
    access_token
    error
  }
}

実行

結果

成功

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

失敗

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

認可ルートを設定

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

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

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

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

結果

おわり

schema.graphqlを変更したら叩く

php artisan lighthouse:clear-cache