Ruby
Ruby

Reputation: 2959

Remove Cookies and Sign out server-side in Next.js

I need to check if there's a token and if the token expired in getInitialProps, and then if the token is expired clear the Cookies from the browser and sign out the user.

Here's what I'm doing so far ...

const isTokenExpired = (token) => {
    const currentTime = new Date().getTime() / 1000;
    const decodedToken: MetadataObj = jwt_decode(token);
    if (decodedToken.exp < currentTime) {
        return true;
    } else {
        return false;
    }
};

import nextCookie from "next-cookies";
import Cookies from "js-cookie";

MyApp.getInitialProps = async ({ctx}: any) => {
    const { WebsiteToken } = nextCookie(ctx);
    if (WebsiteToken ) {
        if (isTokenExpired(WebsiteToken )) {
            console.log("Token Expired");
            Cookies.remove("WebsiteToken ");
        }    
    }

}

The console log works, but the Cookie is not removed from the browser, I know that this is because it didn't hit the client-side, but how can I do that from the server?

Upvotes: 6

Views: 17150

Answers (3)

Denys Skulimovskyi
Denys Skulimovskyi

Reputation: 47

I've spent quite a lot of time on this issue. Finally, the mix of server-side and client-side code works for me. First, return an error on the server side. Then, check it inside the client-side component. I hope it will be helpful for someone else.

// Server-side code: pages/api/auth/[...nextauth].js

import NextAuth from 'next-auth';
import KeycloakProvider from 'next-auth/providers/keycloak';

const ISSUER = '...';
const REALM = '...';

export default async function auth(req, res) {
  async function createProviders() {
    const config = {
      clientId: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
      issuer: ISSUER,
      realm: REALM,
    };

    return [KeycloakProvider(config)];
  }

  async function refreshAccessToken(token, issuer) {
    try {
      /// ... attempt to refresh token here
    } catch (error) {
      return {
        ...token,
        // This error will be catched on the client-side
        error: 'REFRESH_TOKEN_ERROR',
      };
    }
  }  

  const providers = await createProviders();
  const authOptions = {
    providers,
    callbacks: {
      async session({ session, token }) {
        // ...
        return session;
      },
      async jwt({ token, user, account }) {
        // Initial sign in
        if (account && user) {
          // ...
          return {
            accessToken: account.access_token,
            accessTokenExpires: account.expires_at * 1000,
            refreshToken: account.refresh_token,
            user,
            realm: REALM,
          };
        }

        // Return previous token if the access token has not expired yet
        if (Date.now() < token.accessTokenExpires) {
          return token;
        }

        // Access token has expired, try to update it
        return refreshAccessToken(token, ISSUER);
      },
    },
    pages: {
      signIn: '/auth/signin',
    },
  };

  return NextAuth(req, res, authOptions);
}



// Client-side code
'use client';
import { useRouter } from 'next/navigation';
import { useSession, signOut } from 'next-auth/react';
import React, { useEffect } from 'react';
import { Page } from '...';

function LoggedIn() {
  const { data } = useSession({ required: true });
  const router = useRouter();

  useEffect(() => {
    // check for error
    if (data?.error === 'REFRESH_TOKEN_ERROR') {
      signOut();
    }
  }, [data, router]);

  if (!data) return null;

  return <Page />;
}

Upvotes: 0

Ashraf Zaman
Ashraf Zaman

Reputation: 61

For my case, for NextJS 12.3.1 I did the following way

import type { NextResponse } from 'next/server'

export async function deleteCookie(res: NextResponse) {
    res.cookies.set('cookie_name', '', {
    httpOnly: true,
    maxAge: 0, // 0 second hours in seconds
  })
  return res
}

Upvotes: 0

Luka
Luka

Reputation: 173

You can erase cookies by setting the header:

ctx.res.setHeader(
  "Set-Cookie", [
  `WebsiteToken=deleted; Max-Age=0`,
  `AnotherCookieName=deleted; Max-Age=0`]
);

It will set the cookie value to be "deleted" but that is not important because Max-Age=0 means that cookie will expire immediately and browser will delete it.

Upvotes: 12

Related Questions