📚

Rails + TypeScriptでAjax通信のためのCSRFトークンを取得するテンプレートコード

2022/01/23に公開

課題

  • Rails + TypeScriptでajax通信を行うときにcsrfトークンを付与せずに通信しようとすると InvalidAuthenticityToken になる。
    • おもむろに手癖で axios.post("/api/hoge",params) とかやると踏みがち
  • CSRFトークンは、ページのheadタグのmeta[name='csrf-token']" 内にあるため、それを取得して返す関数を作成する

回答

export const getCsrfToken = (): string => {
  const metaElem = document.querySelector("meta[name='csrf-token']")
  if (!metaElem) {
    throw Error("meta[name=csrf-token] is not founded.")
  }
  const csrfToken = metaElem.getAttribute("content")
  if (!csrfToken) {
    throw Error("csrf token is not set")
  }
  return csrfToken
}

  • axios側の設定
import axios, {AxiosRequestConfig} from "axios";
import {getCsrfToken} from "./getCsrfToken";

const defaultOptions: AxiosRequestConfig = {
  headers: {'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': getCsrfToken()}
};

export const client = axios.create(defaultOptions);

作成後は、 client を各コードで import すれば、呼び出し側はCSRFトークンの存在を意識せずに .get, .post, .patch, .delete ができるようになる。

ハマりポイント解説

  • document.querySelector は nullかもしれない ので後続でnullチェックをしないとコンパイルが通らない。
  • 大前提として、この要素が取得できないのはバグのことが多い(呼び出し側ではどうすることもできない)ので、関数内で例外を投げてしまう
  • 要素が取得できたとしても、getAttributeもまたnullを返すことがあるのでnullチェックをしないとコンパイルが通らない。
  • content属性が正しく取得できないのもまた根本的なバグであるため、関数内で例外を投げる
  • このようにすることで、呼び出し側は string で csrfTokenを取得できるようになるため、呼び出し側でNULLチェックを書かなくても良くなる。

参考

CSRF対策などの詳しい解説は↓
Railsガイド クロスサイトリクエストフォージェリ (CSRF)

Discussion