hantoren
hantoren

Reputation: 1265

Unable to delete cookie using Next.js server side action

I'm trying to delete a cookie using the next/headers module in a Next.js application, but it doesn't seem to work as expected. Here's the code snippet:

import {cookies} from "next/headers";
export default async function Signout() {
    async function deleteTokens() {
       "use server"

        cookies().delete('accessToken')
    }

  await deleteTokens()
  return (
      <></>
  );
}

I expected the cookies().delete('accessToken') line to delete the cookie named "accessToken", but it doesn't seem to have any effect. The cookie is still present after the function is executed.

I am getting following error: Error: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options

But i have set the "use server" and enabled it in the next.config.js

#using Next.js 13.4.4

Upvotes: 12

Views: 13896

Answers (4)

ahmad ghasemi
ahmad ghasemi

Reputation: 29

To address the issue of being unable to delete a cookie using Next.js server-side action, you can utilize the following approach:

'use server';
    
import { cookies } from 'next/headers';

/**
 * Function to remove JWT cookies.
 * @returns {Promise<void>} A Promise that resolves once cookies are deleted.
 */
 
export async function removeJWTCookies() {
  // Retrieve all cookies
  const allCookies = cookies().getAll();

  // Iterate through each cookie
  allCookies.forEach((cookie) => {
    // Delete the cookie
    cookies().delete(cookie.name);
  });
}

This solution utilizes the next/headers module to handle cookies. The removeJWTCookies function retrieves all cookies using cookies().getAll(), iterates through them, and deletes each cookie individually. This approach ensures that JWT cookies are properly removed from the server-side context.

Upvotes: -1

This works for me, I use a cookie httpOnly: true, sameSite: 'lax' and domain: '.exampledomain.com' to allow my subdomains to access the cookie.

Cookie definition:

res.cookie('token', token, {
    maxAge: 60 * 60 * 1000, // 1hour (in milliseconds)
    httpOnly: true,
    secure: true, // Set to true if using HTTPS
    sameSite: 'lax',
    domain: '.exampledomain.com'
});

To delete the cookie in Nextjs server action:

'use server';
import { cookies } from 'next/headers'
import { redirect } from 'next/navigation';

const logout = async () => {
    cookies().delete({ name: 'token', domain: '.exampledomain.com', path:'/' });
    redirect('/login');
}
export default logout;`

check in the inspector the name, domain, path

Upvotes: 0

Igor Danchenko
Igor Danchenko

Reputation: 3428

The issue here is that your function runs as part of the server component, not as a server action. You can execute your function as a server action by passing it down to a client component and calling it from a client component on page load.

// app/signout/page.js

import { cookies } from "next/headers";

import SignOutAction from "./SignOutAction";

export default async function SignOut() {
  async function deleteTokens() {
    "use server";

    cookies().delete("accessToken");
  }

  return <SignOutAction deleteTokens={deleteTokens} />;
}
// app/signout/SignOutAction.js

"use client";

import { useEffect, useRef } from "react";

export default function SignOutAction({ deleteTokens }) {
  const deleteTokensRef = useRef(deleteTokens);

  useEffect(() => {
    deleteTokensRef.current = deleteTokens;
  });

  useEffect(() => {
    deleteTokensRef.current();
  }, []);

  return null;
}

P.S. This is not directly related to your original question, but this implementation is prone to CSRF vulnerability - https://security.stackexchange.com/questions/62769/should-login-and-logout-action-have-csrf-protection

Upvotes: 3

Yilmaz
Yilmaz

Reputation: 49571

maybe you need to use set method on actions. from here

Good to know: .set() is only available in a Server Action or Route Handler

To "delete" a cookie, you must set a new cookie with the same name and an empty value. You can also set the maxAge to 0 to expire the cookie immediately.

example from same docs:

'use server'
 
import { cookies } from 'next/headers'
 
async function create(data) {
  cookies().set({
    name: 'name',
    value: '',
    expires: new Date('2016-10-05'),
    path: '/', // For all paths
  })
}

from this issue, it might after 13.4.2

Upvotes: 0

Related Questions