Axios and NextJS + Sanctum cookie: it's discarding baseURL and call wrong domain route

1k views Asked by At

I have an API built with Laravel and Sanctum.

Front-end is built in NextJS. CORS and communication is doing fine - apparently, I can't see errors even with POST requests.

Login goes fine, Laravel sets a cookie for .mydomain.com, path "/", and it's HTTP Only. Nice. No errors in the console. I can see the cookie in the chrome dev tools with the JWT from Laravel Sanctum.

Ok, so after submit the Login form and receive the success response with the jwt cookie/token, the user is redirected to the home page. In NextJS Home page, I have a function to check if the session still valid in Laravel API. This function run inside getServerSideProps hook, a.k.a. NodeJS. To do that, a POST request is made from NextJS server (NodeJS) to Laravel API, trying to get information about the user. In this request, the cookies are "forwarded" to Laravel. But here is the strange thing: the request never gets to Laravel. It is "hitting" NextJS API routes instead. And, of course, the route doesn't exist in Next. It should request Laravel API.

So, step by step:

  1. User types mydomain.com/login and gets the Login page from NextJS.
  2. User fulfills and submits the form. React (client-side) calls Laravel using Axios at api.mydomain.com/api/login.
  3. Laravel API validates the form and sets a jwt cookie in the response having:
 3.1) HTTP Only
 3.2) Path: `/`
 3.3) Domain: `.mydomain.com` (accordingly to `RFC 6265`
 3.4) Value: the JWT token from Laravel Sanctum
  1. After React gets the login response with the jwt cookie, React code redirects the user to mydomain.com/ (root URL). Here is the beginning of the problem.

  2. NextJS will run/build the homepage on server-side. During the SSR in NextJS, the Homepage makes use of getServerSideProps hook in NextJS, and it tries to call api.mydomain.com/api/me to validate the session/cookie/jwt. But, for an unknown reason, it actually calls mydomain.com/api/me. Note that my API runs at the subdomain api.mydomain.com and NextJS at mydomain.com.

It looks like Axios is 'ignoring' the baseURL config. But I even replaced the relative URL with the absolute path, hardcoded, subdomain. Still see the same error. First, it was resulting in a 404 page - because NextJS doesn't have a /pages/api/me.ts file. It took me a giant time to think: "Hey, maybe this request is going to NextJS itself and not Laravel API". So, I put in NextJS a file that outputs the following sentence:

EERRRROOORRRRRR!!!! THIS SHOULD NEVER BE CALLED, SINCE LARAVEL SHOULD ANSWER THIS CALL, NOT NEXTJS API!!!!!

and I could see this response in the logs. Here is my HTTP method:


export async function me(headers: Headers = {}): Promise<User> {
  try {
    const config = {
      baseURL: 'api.mydomain.com/api',
      withCredentials: true,
      headers: {
        ...headers,
      },
    };
    console.log('config', config);

    // I was using a global configured instance, but now I'm creating a new one here:
    const tempAxios = axios.create(config);

    console.log('call HTTP me(). Config:', config);
    // the "space" between // and api is only because Stackoverflow blocks URL in the post
    const { data } = await tempAxios.get('https:// api.mydomain.com/api/user');
    return Promise.resolve(data);
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  } catch (error: any) {
    console.error('call HTTP me() ERROR', error.config);
    return Promise.reject(error);
  }
}

Then, I can see in NextJS / Node logs:

[app-frontend] [2022-03-03 23:39:12] call HTTP me(). Config: {
[app-frontend] [2022-03-03 23:39:12]   baseURL: 'api.mydomain.com/api',
[app-frontend] [2022-03-03 23:39:12]   withCredentials: true,
[app-frontend] [2022-03-03 23:39:12]   headers: {
[app-frontend] [2022-03-03 23:39:12]     host: 'mydomain.com',
[app-frontend] [2022-03-03 23:39:12]     'accept-encoding': 'gzip',
[app-frontend] [2022-03-03 23:39:12]     'x-forwarded-for': '2001:818:e889:6600:f4f5:79c0:beb7:etc etc etc',
[app-frontend] [2022-03-03 23:39:12]     'x-forwarded-proto': 'https',
[app-frontend] [2022-03-03 23:39:12]     'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"',
[app-frontend] [2022-03-03 23:39:12]     'sec-ch-ua-mobile': '?0',
[app-frontend] [2022-03-03 23:39:12]     'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
[app-frontend] [2022-03-03 23:39:12]     'sec-ch-ua-platform': '"macOS"',
[app-frontend] [2022-03-03 23:39:12]     accept: '*/*',
[app-frontend] [2022-03-03 23:39:12]     'sec-fetch-site': 'same-origin',
[app-frontend] [2022-03-03 23:39:12]     'sec-fetch-mode': 'cors',
[app-frontend] [2022-03-03 23:39:12]     'sec-fetch-dest': 'empty',
[app-frontend] [2022-03-03 23:39:12]     referer: 'mydomain.com/login',
[app-frontend] [2022-03-03 23:39:12]     'accept-language': 'pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7',
[app-frontend] [2022-03-03 23:39:12]     cookie: 'jwt=75%7C4VyknPf8Jb2papbla-bla-bla',
[app-frontend] [2022-03-03 23:39:12]     'cdn-loop': 'cloudflare',
[app-frontend] [2022-03-03 23:39:12]     'do-connecting-ip': '2001:818:etc',
[app-frontend] [2022-03-03 23:39:12]     'Content-Type': 'application/json',
[app-frontend] [2022-03-03 23:39:12]     Accept: 'application/json'
[app-frontend] [2022-03-03 23:39:12]   }
[app-frontend] [2022-03-03 23:39:12] }
[app-frontend] [2022-03-03 23:39:12] EERRRROOORRRRRR!!!! THIS SHOULD NEVER BE CALLED, SINCE LARAVEL SHOULD ANSWER THIS CALL, NOT NEXTJS API!!!!!

The last line of the logs above only exists because Axios is calling the wrong domain during NextJS SSR. So, whatever I do, the request is always hitting mydomain.com/api/me and never the subdomain/Laravel API at api.mydomain.com. I'm doing all of this in DigitalOcean Apps platform. Using the console, I could manually run a debug.js file like node debug.js and it could fetch correctly the URL/payload. But, inside NextJS, the baseURL is ignored, and the request always goes to NextJS.

I don't know what else I should do here. Because the login route goes fine. I'm not sure if it's because of the config.headers.host that is preventing the request to go to the subdomain.

Any help is very appreciated.

0

There are 0 answers