30
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Nuxt.js で axios のヘッダに認証用トークンを付与したりトークンを更新したりを共通化する

Last updated at Posted at 2019-08-13

やりたいこと

  • Nuxt.js からリソースサーバーにリクエストするときにヘッダーにトークンを付与したい。
  • トークンの有効期限が切れていたら更新したい。

課題

リソースサーバーへリクエストするコンポーネント or ストアから axios にヘッダーを設定したり、コールバックをハンドリングするのはなかなか大変なので一箇所にまとめたい。

解決方法

interceptors

axios には interceptors という仕組みがあり、リクエスト、レスポンスをハンドリングする前に処理を割り込みさせることが可能です。これを利用することで共通化することができます。

You can intercept requests or responses before they are handled by then or catch.

全てのリクエストにヘッダーの付与する

  axios.interceptors.request.use((config: AxiosRequestConfig) => {
    config.headers = { token: token } // todo: ここでトークンを付与する
    return config
  })

トークンの有効期限が切れていたら更新する

  axios.interceptors.response.use((response => response), (error => {
    if (error.response.status === 401) {
      // todo: トークンの更新処理 
      return;
    }
  }))

API クライアントモジュールを作成

上記の interceptors の設定は plugins/ 配下に api.ts のようなファイルを作成しました。これを nuxt.config.jsplugins プロパティに追記することでアプリ起動時にロードされます。

nuxt.config.js
export default {
  plugins: [
    { src: '~/plugins/api.ts' },
  ]
}

実際のコード

リクエストに共通のヘッダーを付与する

実際のユースケースを考慮すると、多くの場合が Cookie や Vuex に保存済みのトークンをリクエストに付与するのではないでしょうか。その場合、 plugins プロパティは Nuxt のコンテキストを引数に取ることができます。つまり store も取得可能です。

~/plugins/api.ts

export default function setup({ store }) {
  api.interceptors.request.use((config: AxiosRequestConfig) => {
    config.headers = { token: store.state.user.token }
    return config
  })
}

それ以外 plugins で受け取ることができるパラメータは公式のこちらのページにあります。

アクセストークンを更新する

アクセストークンの有効期限が切れたら、トークンを更新する必要があります。
トークン更新の方法は公式の Issue にあったものがわかりやすかったので、これをベースに少し修正を加えます。

~/plugins/api.ts
let id

export default function setup({ store }) {
  if (!!id || id === 0) {
    return
  }

  id = axios.interceptors.response.use(null, (error) => {
    if (error.config && error.response && error.response.status === 401) {
      return updateToken().then((newToken) => {
        store.dispatch('user/userToken', newToken) // トークンを保存
        error.config.headers.xxxx // ヘッダーに新しいトークンを追加
        return axios.request(error.config);
      });
    }
    return Promise.reject(error);
  });
}

ポイント:認証エラー時にはアクセストークンを更新してからリトライをしたいので、 error が発生したリクエストの情報が必要になります。その情報は error.config にあるのでこれを axios.request() のパラメータに渡してあげればリトライが可能です。

リダイレクトする

アクセストークンの更新に失敗した場合やトークンを更新する必要がない場合、ユーザー認証画面へ飛ばしたいというケースがあると思います。

そんな時は plugins 配下のファイルであれば Nuxt の Context を受け取ることができるので redirect を利用することが可能です。

~/plugins/api.ts
let id

export default function setup({ store, redirect }) {
  if (!!id || id === 0) {
    return
  }

  id = axios.interceptors.response.use(null, (error) => {
    if (error.config && error.response && error.response.status === 401) {
      redirect('/')
    }
    return Promise.reject(error);
  });
}

追記

  • axios.interceptors.response.use を追加するかどうかの判定処理を追加しました。
  • メモリリークの原因となります。詳細は こちら

参考

この記事を書くにあたり参考にしたものです。

https://ja.nuxtjs.org/guide/plugins/
https://ja.nuxtjs.org/api/configuration-plugins/
https://github.com/axios/axios#interceptors
https://github.com/axios/axios/issues/934#issuecomment-322003342

30
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?