はじめに
Remixでアプリケーションを作っているのですが、Cookie周りで苦戦したのでまとめていきます
問題
RemixはステートではなくCookieで値を保持しておくことが多いため、バックエンドサーバーから送られてきたセッションIDをログイン時にCookieに保存して、トップページに遷移するようなコードを書きました
login.tsx
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const email = formData.get("email");
const password = formData.get("password");
const response = await fetch(`${process.env.BE_HOST}/api/users/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ user: { email, password } }),
});
if (response.ok) {
const sessionData = response.headers.get("Set-Cookie");
if (!sessionData) {
return json({ error: "ログインに失敗しました" }, { status: 500 });
}
const sessionId = sessionData.split("_session_id=")[1].split(";")[0];
return redirect("/", {
headers: {
"Set-Cookie": sessionId
},
});
} else {
return json(
{ error: "メールアドレスまたはパスワードが不正です" },
{ status: 401 }
);
}
};
そしてログイン時にすでにログイン済みかを以下の関数でチェックするようにしました
export async function requireUserSession(request: Request) {
const sessionId = await getUserFromSession(request);
console.log(`sessionId: ${sessionId}`);
if (!sessionId) {
throw redirect("/login");
}
return sessionId;
}
export async function getUserFromSession(request: Request) {
const cookie = request.headers.get("Cookie");
const sessionId = cookie?.split("_session_id=")[1];
if (!sessionId) {
return null;
}
return sessionId;
}
しかしこの方法でやるとローカルではうまく行くのですが、ステージング環境にデプロイした時になぜかCookieに保存がされずに、sessionIdがnull
となってしまいログインページにリダイレクトされてしまいました
解決方法
ドキュメント通りに実装していくことで解決しました
login.tsx
export const action = async ({ request }: ActionFunctionArgs) => {
const formData = await request.formData();
const email = formData.get("email");
const password = formData.get("password");
const response = await fetch(`${process.env.BE_HOST}/api/users/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ user: { email, password } }),
});
if (response.ok) {
const sessionData = response.headers.get("Set-Cookie");
if (!sessionData) {
return json({ error: "ログインに失敗しました" }, { status: 500 });
}
const cookieHeader = request.headers.get("Cookie");
const cookie = (await userPrefs.parse(cookieHeader)) || {};
cookie.epSessionId = sessionData.split("_session_id=")[1].split(";")[0];
return redirect("/", {
headers: {
"Set-Cookie": await userPrefs.serialize(cookie),
},
});
} else {
return json(
{ error: "メールアドレスまたはパスワードが不正です" },
{ status: 401 }
);
}
};
import { createCookie } from "@remix-run/node";
import { redirect } from "@remix-run/react";
export async function requireUserSession(request: Request) {
const sessionId = await getUserFromSession(request);
console.log(`sessionId: ${sessionId}`);
if (!sessionId) {
throw redirect("/login");
}
return sessionId;
}
export const userPrefs = createCookie("user-prefs", {
maxAge: 604_800,
});
export async function getUserFromSession(request: Request) {
const cookieHeader = request.headers.get("Cookie");
const cookie = (await userPrefs.parse(cookieHeader)) || {};
const sessionId = cookie.epSessionId;
if (!sessionId) {
return null;
}
return sessionId;
}
おわりに
私は以下のZennも参考にしたのですが、なぜかデプロイするとCookieが保持できていませんでした
結果的には解決できたのでハードルを超えられたので良かったです
参考