s_kajigo
@s_kajigo (kaji shogo)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Rails APIモードでのCRSF対策で、ActionController::BaseとすべきかAPIとすべきか

現在Rails apiとreactでSPA構成のポートフォリオ作成に取り組んでいます。
railsをapiモードで立ち上げ、ログイン機能を、railsのdevise,devise_token_authを使用してcookieにaccess-tokenが保存される形で実装しました。
react側では、cookieに保存されたtoken情報をリクエストヘッダーとして投げることでユーザーを判別し、ユーザーが存在すればstateを更新してログイン状態にしたり、個別のユーザーデータを操作できるようにしています。

今回知りたいこと
1、APIモードで立ち上げたが、ApplicationControllerの継承元をActionController::APIと変更せずにrailsのデフォルトのCSRF対策を利用できるのか?
2、そもそも現在制作中のサイトの認証方式ではCookieを使用しているが、session情報ではなくトークンを使用しており、CSRF対策は不要なのか?
ということです。

制作する上で、参考にしたサイトによって継承するActionController::がBaseかAPIと異なっており、で、CSRF対策について調べていると、
APIとする場合viewやcookieに関する機能と同時に、CSRF対策も取り除かれてしまうため自分でprotect_from_forgeryメソッドを使用できるようincludeし、CSRFトークンが生成された上で通信されるように設定する必要があることを知りました。
参考にさせていただいた記事

ActionController::APIとしている記事
(https://zenn.dev/shogo_matsumoto/articles/c6485b39c5f621)

ActionController::Baseとしている記事
("https://qiita.com/kazama1209/items/86c0adc8b6d38fceb36a")
CSRFについて参考にした記事
("https://midorimici.com/posts/rails-api-csrf")

以下現時点で記載しているコードです。

auth.js
import { client } from "./client";
import Cookies from "js-cookie";

// サインアップ
export const signUp = (params) => {
 return client.post("/auth", params);
};

// サインイン
export const signIn = (params) => {
 return client.post("/auth/sign_in", params);
};

// サインアウト
export const signOut = () => {
 return client.delete("/auth/sign_out", {
  headers: {
   "access-token": Cookies.get("_access_token"),
   client: Cookies.get("_client"),
   uid: Cookies.get("_uid"),
  },
 });
};

// Cookieに必要なデータセットがあるかを確認。揃っていればrailsに投げてcurrentUserがいるかを検索。
export const getCurrentUser = () => {
 if (
  !Cookies.get("_access_token") ||
  !Cookies.get("_client") ||
  !Cookies.get("_uid")
 )
  return console.log("ログイン中のユーザーはいません。");
 return (
  client.get("/auth/sessions", {
   headers: {
    "access-token": Cookies.get("_access_token"),
    "client": Cookies.get("_client"),
    "uid": Cookies.get("_uid"),
   },
  })
 )
};
App.js
import { createContext, useEffect, useState } from "react";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import { getCurrentUser } from "./api/auth";
import { Home } from "./components/Home";
import { SignIn } from "./components/SignIn";
import { SignUp } from "./components/SignUp";
import { Top } from "./components/Top";

export const AuthContext = createContext();

function App() {
  const [loading, setLoading] = useState(true);
  const [isSignedIn, setIsSignedIn] = useState(false);
  const [currentUser, setCurrentUser] = useState();

  // Cookieからログイン中ユーザーを取得、結果をハンドリング
  const handleGetCurrentUser = async () => {
    try {
      const res = await getCurrentUser();
      if (res?.status === 200) {
        setIsSignedIn(true);
        setCurrentUser(res?.data.currentUser);
        console.log(res?.data.currentUser);
      } else {
        console.log("no current user");
      }
    } catch (e) {
      console.log(e);
    }
    setLoading(false);
  };

  useEffect(() => {
    handleGetCurrentUser();
  }, [setCurrentUser]);

  const Private = ({ children }) => {
    if (!loading) {
      if (isSignedIn) {
        return children;
      } else {
        return <Redirect to="/signin" />;
      }
    } else {
      return <></>;
    }
  };
  return (
    <AuthContext.Provider
      value={{
        loading,
        setLoading,
        isSignedIn,
        setIsSignedIn,
        currentUser,
        setCurrentUser,
      }}
    >
      <BrowserRouter>
        <Switch>
          <Route exact path="/top">
            <Top />
          </Route>
          <Route exact path="/signin">
            <SignIn />
          </Route>
          <Private>
            <Route exact path="/">
              <Home />
            </Route>
          </Private>
        </Switch>
      </BrowserRouter>
    </AuthContext.Provider>
  );
}

export default App;

api

application_controller.rb
class ApplicationController < ActionController::Base
         protect_from_forgery
         include DeviseTokenAuth::Concerns::SetUserByToken

         helper_method :current_user, :user_signed_in?
 end

理解が浅く的をいていない記載があるかと思いますがご教授いただけたら幸いです。

0

1Answer

ApplicationControllerの継承元をActionController::Baseと変更してrailsのデフォルトのCSRF対策に乗っかる方がいいのではないか?

ActionController::Base から別のクラスへの変更ですか?
それか ActionController::Base への変更?

デフォルトが ActionController::Base ですよね

1Like

Comments

  1. @s_kajigo

    Questioner

    回答ありがとうございます。
    失礼しました誤記ですので修正しました。
    参考サイトではAPIへと変更していたのですが、変更せずBaseのまま進めようかと悩んでおります。
  2. >制作する上で、参考にしたサイトによって継承するActionController::がBaseかAPIと異なっており、で、CSRF対策について調べていると、

    参考にしたサイトのURLもあると、回答がつきやすくなるかと思います!
  3. @s_kajigo

    Questioner

    ありがとうございます。追加させていただきます!
  4. リンク切れしてるみたいです!
  5. @s_kajigo

    Questioner

    申し訳ありません!貼り直しましたのでご確認ください。

Your answer might help someone💌