GraphQL と Laravel で JWT を使おうとしたらハマったお話

GraphQL と Laravel で JWT を使おうとしたらハマったお話

何時間もハマったので共有したくてメモしました。あとから考えると、CORSを全然理解できていなかったのがハマった原因でした。
理解している自信ないけどまあ開発できてるし、、、みたいなものはちゃんと勉強しておかないですね。

利用言語とオリジンはこんな感じです
フロント
 利用言語:Vue(Nuxt)、GraphQL
 オリジン:https://xxx.posse-ap.com/
バックエンド
 利用言語:PHP(Laravel)
 オリジン:https://api.xxx.posse-ap.com/
フロントとバックエンドのオリジンが異なるので、CORSを意識した実装が必要になりますね

JWTのトークン取得からローカルストレージへの保存は問題なく実装できて、
ブラウザのローカルストレージに保管してあるJWTを、ヘッダーに詰めてバックエンドにリクエスト投げるところで問題が出ました。
実装は以下のようにしました

  const authMiddleware = new ApolloLink((operation, forward) => {
    // add the authorization to the headers
    operation.setContext({
      headers: {
        authorization: 'Bearer ' + localStorage.getItem('token'),
      },
    })

    return forward(operation)
  })

  return {
    link: ApolloLink.from([cleanTypeName, authMiddleware, httpLink]),
    cache: new InMemoryCache(),
    defaultHttpLink: false,
  }
apollo.config.js

これでGraphQLのapolloを使ってバックエンドに通信するときに、勝手にヘッダーにトークンがつくはず!簡単!!
早速通信してみます

通信ができていないようで画面が表示されません、ChromeのデベロッパーツールでNetworkタブを確認します

思いっきり CORS error って出ているので確認していきます。ヘッダーにトークンつける前はもちろんちゃんと動いていました。

ヘッダーに origin とかの記載もないしなんだかすごく少ないです。
apolloの設定ファイルをいじったせいで、必要なヘッダー情報まで消えちゃったと思ったのですが、これが間違いでした。

ここからapollo.config.jsの書き方を変えて色々試しました。setContextすると他のヘッダーが消えちゃうのかな、、、でも検索して出てくるサンプルはみんな同じように書いてるんだけど。。。と調べてましたが、手詰まりになりました。
一旦諦めて、試しにaxiosでヘッダーにトークンつけて通信させてみると、同じ事象が発生しました。これでバックエンドが怪しくなりました。

とはいえ、対象の通信がサーバに行く前にヘッダー消えてるんですよね。なのでこの通信とは別に通信していたりするのかもと思い、preflight を確認してみました。

内容はこんな感じです。
ヘッダーにトークンをつける前後で比較してみると、修正後だけ access-control-request-headers にauthrizationがついていました。
Laravelでこれをチェックしていたりするのかと思って調べてみると、チェックしてました。こちらです。

class Cors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
            ->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
            ->header('Access-Control-Allow-Headers', 'Content-Type');
    }
}
Cors.php

Access-Control-Allow-Headers で Content-Type だけ許可するようになっているので、ここに authorization を追加したら無事通信できました!

いやーはまりました。CORSについてしっかり勉強しろ!!って言われている気持ちになりました。