1

Axios is not sending the cookie to my backend API even when setting the withCredentials properly.

Front-end (NextJS):

import axios from "axios"

const BASE_URL = "http://localhost:8000"

export default axios.create({
  baseURL: BASE_URL,
  headers: { "Content-Type": "application/json" },
  withCredentials: true,
})

Front-end Login (NextJS - Next Auth)

async authorize(credentials, req) {
        const res = await axios.post(
          "auth/signin",
          {
            email: credentials?.email,
            password: credentials?.password,
          }
        )
        console.log(res.headers)
        const user = await res.data
        if (user) {
          return user
        } else {
          return null
        }
      },

In the above code, when I use consloe.log to read the headers, I can clearly see that I am getting the cookie from the backend correctly:

AxiosHeaders {
  'x-powered-by': 'Express',
  'access-control-allow-origin': 'http://localhost:3000',
  vary: 'Origin',
  'access-control-allow-credentials': 'true',
  'set-cookie': [
    'site-auth.session-token=j%3A%7B%22accessToken%22%3A%22eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzaTNqc255cm50djdwMTRmenljY2UybWQiLCJlf_KU2ygyjCp096shZCXL_kQfR1U3LdYLSbPQ5VTSo%22%2C%22tokenId%22%3A%22clksubs1x00008z8nmospe1yk%22%7D; Domain=localhost; Path=/; Expires=Tue, 08 Aug 2023 21:58:03 GMT; HttpOnly; Secure; SameSite=None'
  ],
  'content-type': 'application/json; charset=utf-8',
  'content-length': '175',
  etag: 'W/"af-sE/2jElZ1eZqOhODKrhXa4Tz3xw"',
  date: 'Tue, 01 Aug 2023 21:58:03 GMT',
  connection: 'close'
}

But when I try to test and get the User Profile, I am getting an error from Axios indicating that the API has rejected the request. When I checked the API logs, I can see that the cookie is not being passed to it.

Front-end Axios Calling the Profile End-point:

const getProfile = async () => {
    const res = await axios.get("/user/profile")
    setProfile(res.data)
  }

However, I noticed something important. My API is getting other cookies from Next-Auth, which means that Axios is in fact is sending cookies, but not the one I created.

Also, I can see the list of cookies in the browser (Chrome) console, but not the cookie I created

enter image description here

For some reason, it seems that my cookie is not being saved or Axios is not saving it or sending it.

I have tested my API using Postman and everything is fine and I can see the cookie being generated and used properly by Postman.

Here are some more information about the backend API (NestJS)

Back-end API (NestJS)

  app.enableCors({
    origin: 'http://localhost:3000',
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
    preflightContinue: false,
    credentials: true,
    optionsSuccessStatus: 204,
  });

Back-end Generating Cookie (NestJS)

res.cookie(
      'site-auth.session-token',
      {
        accessToken,
        refreshToken,
        tokenId,
      },
      {
        httpOnly: true,
        sameSite: 'none',
        domain: 'localhost',
        secure: true,
        expires: this.getRefreshExpiry(),
      },
    );

Please help. I have been stuck for hours and I am unable to proceed with my app. I googled a lot but not hope.

Yousi
  • 811
  • 3
  • 12
  • 26
  • Does the sameSite:'none' require a secure connection? https instead of http? your client is on http local host – Nazrul Chowdhury Aug 02 '23 at 09:26
  • Well, it shouldn't, I know. However, I tried it both ways and it did not make a difference. The cookie is still not being saved in the browser for some reason. However, other cookies are being sent normally. – Yousi Aug 03 '23 at 06:23

2 Answers2

0

For Next.js, it's better to handle the authentication using NextAuth.js. Because NextAuth.js and Axios solve different parts of the development process.

Axios is a promise-based HTTP client for the browser and Node.js, making it an excellent tool for making HTTP requests. But it will little tricky to deal with auth especially you just started using Next.js.

On the other hand, NextAuth.js is a complete open-source authentication solution for Next.js applications. It's designed to support various providers, JWT sessions, and database adapters with a minimal API.

So my suggestions, to solve this problem easier, better to use NextAuth.js to deal authentication. As in your use case, you are using Nest.js as backend, it would be a complete match using NextAuth.js as your Next.js library that handle authetication.

0

Since the login request in being made on the server-side of NextJS (the one inside authorize method of NextAuth) the cookie received from your backend is set on the NextJS server, so you have to forward it to the NextJS client session.

Now to do that, it depends on which version of NextJS Router you are using:

Pages Router: Here is a good example on how to do it

In short: Modify the [...nextauth] route handler to have access to the response that will be sent to client and set the cookie on it.

import { NextApiRequest, NextApiResponse } from 'next';
import NextAuth, { NextAuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import axios from 'axios'

type NextAuthOptionsCallback = (req: NextApiRequest, res: NextApiResponse) => NextAuthOptions

const nextAuthOptions: NextAuthOptionsCallback = (req, res) => {
     return {
        providers: [
           CredentialsProvider({
                credentials: {
                },
                async authorize(credentials) {
                   try {                      
                        const response = await axios.post('/api/login', {
                            username: credentials.username,
                            password: credentials.password
                        })

                        const cookies = response.headers['set-cookie']
                        // Send cookies to the browser
                        res.setHeader('Set-Cookie', cookies)

                        return response.data
                    } catch (error) {
                        console.log(error)
                        throw (Error(error.response))
                    } 
                }
           })
        ],
    }
}

export default (req: NextApiRequest, res: NextApiResponse) => {
    return NextAuth(req, res, nextAuthOptions(req, res))
}

App Router: I currently do not know a way get access to the client response using the new App Router, but it can be done using NextJS's Middleware and NextAuth Middleware

// middleware.ts
import { NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
import { withAuth } from 'next-auth/middleware';

export default withAuth(
    // `withAuth` augments your `Request` with the user's token.
    async (req) => {
        const response = NextResponse.next();
        const cookies = req.cookies.getAll();

        if (cookies) {
            response.headers.set('Set-Cookie', cookies);
        }

        return response;
    },
);

export const config = {
    matcher: [
        /*
         * Match all request paths except for the ones starting with:
         * - api (API routes)
         * - _next/static (static files)
         * - _next/image (image optimization files)
         * - favicon.ico (favicon file)
         * - .*\\..* (public assets)
         */
        '/((?!api|_next/static|_next/image|.*\\..*|favicon.ico|robots.txt).*)',
    ],
};