0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsとRails APIでセッションCookieを共有する

0
Posted at

はじめに

この投稿は、Next.js学習者が書いています。Rails単体のアプリから、Next.jsをフロントに置き、APIはRailsのまま、という構成へ移したときに、セッションCookieの扱いを、自身の理解のために整理します。

Railsだけだったころは、ブラウザが同一オリジンへのリクエストにCookieを付ける、という流れで足りることが多いです。Next.jsを挟むと、ブラウザから見えるリクエストと、サーバコンポーネントなどサーバ側からRailsへ出すリクエストで、Cookieの運び方が分かれます。

1. 全体の役割分担

想定する構成は次です。

  • ブラウザ … ユーザーの操作、ログイン後の画面
  • Next.js … UIと、一部のサーバ側データ取得
  • Rails … セッション発行とAPI

セッションCookieはRailsが発行し、ブラウザが保持します。以降で見るのは、誰がRailsへのリクエストにCookieを載せるかです。

2. ブラウザからのfetch

ブラウザ上のfetchでは、Cookieを手動でヘッダに書かず、次のようにします。

await fetch("/api/session", {
  method: "POST",
  credentials: "include",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email, password }),
});

ブラウザがfetchで送る/api/*は、Next.jsのrewriteでRailsへ転送されます。ブラウザから見ると、いま開いているサイトへの同一オリジンのリクエストです。

この例はログインです。成功すると、レスポンスヘッダに含まれるSet-Cookieをブラウザが保存します。ログイン後のAPI呼び出しでも、同じcredentials: "include"により、保持しているセッションCookieがリクエストに付きます。

3. サーバ側からのfetch

サーバコンポーネントやサーバ専用モジュールからRailsへfetchする場合、呼び出すのはブラウザではありません。ブラウザが自動で付けるCookieは、そのままでは付きません。

サーバ側では、届いているリクエストのCookieを読み、Rails向けのCookieヘッダとして付け直します。_app_sessionは、Railsのsession_storeで設定するCookie名の例で、Next.js側でも同じ文字列を使います。

import { cookies } from "next/headers";

const SESSION_COOKIE_NAME = "_app_session";

async function sessionCookieHeader(): Promise<
  { Cookie: string } | undefined
> {
  const cookieStore = await cookies();
  const session = cookieStore.get(SESSION_COOKIE_NAME);
  if (!session) {
    return undefined;
  }
  return {
    Cookie: `${SESSION_COOKIE_NAME}=${encodeURIComponent(session.value)}`,
  };
}

async function fetchTasks() {
  const res = await fetch("http://api.example/api/tasks", {
    headers: await sessionCookieHeader(),
    cache: "no-store",
  });
  return res.json();
}

http://api.exampleは説明用のURLです。

ポイントは次です。

  • cookies()で、Next.jsに届いたリクエスト上のセッションを読む
  • 値があるときだけCookieヘッダを組み立て、Railsへ付ける
  • 値はencodeURIComponentで整え、記号を含んでもヘッダとして送れる形にする
  • ないときはヘッダを付けない(未ログインとしてAPI側の応答に任せる)

ブラウザのcredentials: "include"と、サーバのCookieヘッダ付け直しは、同じセッションを運ぶ別経路です。

4. ルート保護での確認

画面遷移の入口で、セッションCookieの有無だけを見ることもあります。次の例は、プロジェクト直下のproxy.tsに置く処理です。

import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

const SESSION_COOKIE_NAME = "_app_session";

export function proxy(request: NextRequest) {
  if (request.nextUrl.pathname === "/login") {
    return NextResponse.next();
  }

  if (!request.cookies.has(SESSION_COOKIE_NAME)) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  return NextResponse.next();
}

ここでも読んでいるのは、ブラウザからNext.jsへ来たリクエストにCookieがあるかどうかです。サーバ側でCookieヘッダを付け直す処理とは別の層ですが、同じCookie名を共有します。

まとめ

  • RailsがセッションCookieを発行し、ブラウザが保持する、という点は従来と同様です。
  • ブラウザからのAPI呼び出しではcredentials: "include"でCookieを付けます。
  • Next.jsのサーバ側からRailsへ出す呼び出しでは、cookies()の値をCookieヘッダとして付け直します。
  • 自動付与に頼れるのはブラウザ起点のリクエストであり、サーバ起点では明示的な付け直しが必要です。
0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?