이승제
이승제

Reputation: 21

Nextjs route handler set cookie not working

I am currently experiencing an issue with setting cookies in a Next.js 14 route handler. I have confirmed through console logs that the token is correctly included in the Set-Cookie header, but the cookie is not appearing in the browser. I have tried adjusting various options like SameSite, secure, and httpOnly, but nothing seems to work. What could be the problem? Below is my code:

// app/auth/login/page.tsx

import { redirect } from 'next/navigation';

import { LoginPage } from 'common';

interface Params {
  searchParams?: { [key: string]: string | undefined };
}

const Login = async ({ searchParams }: Params) => {
  const loginCode = searchParams?.code;

  if (loginCode) {
    const res = await fetch(
      new URL(`/auth/login/post?code=${loginCode}`, process.env.BASE_URL),
      {
        method: 'POST',
      }
    );

    console.log(res.headers.getSetCookie()); // (1)

    redirect('/');
  }

  return <LoginPage />;
};

export default Login;

// app/auth/login/post/route.ts

import { cookies } from 'next/headers';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

import type { TokenResponseLoginType } from 'types';

export async function POST(request: NextRequest) {
  const cookieStore = cookies();

  try {
    .
    .
    .

    const tokenData: TokenResponseLoginType = await res.json();

    cookieStore.set('refresh_token', tokenData.refreshToken);
    cookieStore.set('access_token', tokenData.accessToken);

    // just try. (before -> return Response.redirect . . .)
    return new Response('response', {
      status: 200,
      headers: {
        'Set-Cookie': `refresh_token=${tokenData.refreshToken}; access_token=${tokenData.accessToken}`,
      },
    });
  } catch (e) {
    return NextResponse.redirect(new URL('/auth/login', request.url));
  }
}

(1) ['access_token=blablabla; Path=/']

I feel like I've tried everything regarding cookies. I've adjusted the options when setting the cookies, and I've even switched browsers. Here are the methods I've attempted:

Setting the SameSite option to Strict, Lax, and None. Toggling the secure option on and off. Setting and unsetting the httpOnly option. Checking and adjusting the cookie domain and path. Confirming the Set-Cookie header is properly included in the server response through console logs. Testing across different browsers to see if the issue is browser-specific. Ensuring the server is running in the correct environment (development or production) to match the cookie settings.

Upvotes: 2

Views: 2268

Answers (3)

hansss
hansss

Reputation: 521

Faced the same issue in setting cookies using a route handler in NextJS 14. Maybe its something to do with streaming

Tried a few options:

What works for me is the middleware method but in a single route handler. Something similar to this e.g.

export async function POST(request: NextRequest) {
    // your code 
    ....
    const nextResponse = NextResponse.json(data, {
      headers: request.headers,
    });
    nextResponse.cookies.set("<your-cookie-key>",<your-cookie-value>);

    return nextResponse;
}

Upvotes: 0

Saad Bukhari
Saad Bukhari

Reputation: 11

you can use the route handlers , next js, can send headers , by writing new Headers(res.headers) it will include all the headers from the response and send it to you frontend.

import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { LoginSchema } from "~/schemas";

// Function to include cookies in request headers
const sendCookies = (options?: HeadersInit) => {
  const cookieStore = cookies();
  return {
    Cookie: cookieStore.toString(),
    ...options,
  };
};

// Handle GET requests
export async function GET(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  const pathparts = pathname.split("/");
  const type = pathparts[3];

  try {
    if (type === "session") {
      const res = await fetch("http://localhost:3000/auth/api/session", {
        method: "GET",
        cache: "no-cache",
        headers: sendCookies(),
      });
     
      const body = await res.json();
      
      const headers = new Headers(res.headers);

      const setCookieHeader = headers.get('Set-Cookie');
      
      console.log(setCookieHeader);
      
      return NextResponse.json(
        { user: body.user },
        {
          status: 200,
          headers: {
            'Set-Cookie': setCookieHeader || ''
          },
        },
      );
    }
  } catch (error) {
    if (error instanceof Error) {
      return NextResponse.json(
        { error: error.message },
        { status: 500 }
      );
    }
  }
}

// Handle POST requests
export async function POST(request: NextRequest) {
  const pathname = request.nextUrl.pathname;
  const pathparts = pathname.split("/");
  const type = pathparts[3]; // e.g., "login"

  if (type === "login") {
    try {
      const body = await request.json();
      const validate = await LoginSchema.safeParseAsync(body);

      if (!validate.success) {
        return NextResponse.json(
          { error: validate.error.errors[0].message },
          { status: 403 }
        );
      }

      const res = await fetch("http://localhost:3000/auth/login", {
        method: "POST",
        headers: sendCookies({
          "Content-Type": "application/json",
        }),
        body: JSON.stringify(body), // Include the body for POST request
      });

      const headers = new Headers(res.headers); // it has the cookies from the response
      const result = await res.json();

      return NextResponse.json(result, {
        status: 200,
        headers,
      });
    } catch (error) {
      if (error instanceof Error) {
        return NextResponse.json(
          { error: error.message },
          { status: 500 }
        );
      }
    }
  }
}

Upvotes: 0

Jinseok Seo
Jinseok Seo

Reputation: 91

In order to set the cookies in api route in next js 14. You have to return the response that sets the cookies

//api/[myroute]/route.ts
const res = NextResponse.json({})
const newExpires = new Date(Date.now() + cookieValidTime)
res.cookies.set(
  'session',
  JSON.stringify({
    ...oldSession,
    user: {
      ...oldSession.user,
      ...updatedFields,
      expires: newExpires,
    },
  } as ISession),
  {
    expires: newExpires,
    httpOnly: true,
  }
)
return res

also, just in case the dynamic pages might be cached by next server you can set the dynamic page time to 0 second in next.config.mjs file

//next.config.mjs
experimental: {
        staleTimes: {
          dynamic: 0, // default is 30
          static: 180,
        },
      },

Upvotes: 1

Related Questions