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")
以下現時点で記載しているコードです。
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"),
},
})
)
};
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
class ApplicationController < ActionController::Base
protect_from_forgery
include DeviseTokenAuth::Concerns::SetUserByToken
helper_method :current_user, :user_signed_in?
end
理解が浅く的をいていない記載があるかと思いますがご教授いただけたら幸いです。