Axios 和 NextJS + Sanctum cookie:它正在丢弃baseURL并调用错误的域路由

发布于 2025-01-12 00:19:11 字数 5215 浏览 1 评论 0原文

我有一个用 Laravel 和 Sanctum 构建的 API。

前端是用 NextJS 构建的。 CORS 和通信运行良好 - 显然,即使使用 POST 请求我也看不到错误。

登录顺利,Laravel 为 .mydomain.com 设置了一个 cookie,路径“/”,并且它是 HTTP Only。好的。控制台中没有错误。我可以使用 Laravel Sanctum 的 JWT 在 chrome 开发工具中看到 cookie。

好的,提交登录表单并收到带有 jwt cookie/token 的成功响应后,用户将被重定向到主页。在 NextJS 主页中,我有一个函数来检查 Laravel API 中的会话是否仍然有效。该函数在 getServerSideProps 钩子(又名 NodeJS)内运行。 为此,从 NextJS 服务器 (NodeJS) 向 Laravel API 发出 POST 请求,尝试获取有关用户的信息。在此请求中,cookie 被“转发”到 Laravel。但奇怪的是:请求永远不会到达 Laravel。它是“击中”NextJS API 路由。当然,该路线在 Next 中并不存在。它应该请求 Laravel API。

因此,一步一步:

  1. 用户输入 mydomain.com/login 并从 NextJS 获取登录页面。
  2. 用户填写并提交表格。 React(客户端)使用 Axios 在 api.mydomain.com/api/login 调用 Laravel。
  3. Laravel API 验证表单并在响应中设置一个 jwt cookie,其中包含:
 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. React 使用 jwt cookie 获取登录响应后,React 代码将用户重定向到 < code>mydomain.com/(根 URL)。这就是问题的开始。

  2. NextJS 将在服务器端运行/构建主页。在 NextJS 中的 SSR 期间,主页使用 NextJS 中的 getServerSideProps 挂钩,并尝试调用 api.mydomain.com/api/me 来验证会话/cookie /jwt。但是,由于未知的原因,它实际上调用了 mydomain.com/api/me。请注意,我的 API 在子域 api.mydomain.com 上运行,NextJS 在 mydomain.com 上运行。

看起来 Axios 正在“忽略”baseURL 配置。但我什至用绝对路径、硬编码、子域替换了相对 URL。仍然看到同样的错误。首先,它导致了 404 页面 - 因为 NextJS 没有 /pages/api/me.ts 文件。我花了很长时间才想:“嘿,也许这个请求会发送给 NextJS 本身,而不是 Laravel API”。因此,我在 NextJS 中放入了一个输出以下句子的文件:

呃呃呃呃呃呃!!!这永远不应该被调用,因为 Laravel 应该回答这个调用,而不是 NEXTJS API!!!!!!

我可以在日志中看到这个响应。这是我的 HTTP 方法:


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);
  }
}

然后,我可以在 NextJS / Node 日志中看到:

[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!!!!!

上面日志的最后一行仅存在,因为 Axios 在 NextJS SSR 期间调用了错误的域。因此,无论我做什么,请求总是命中 mydomain.com/api/me 而不是 api.mydomain.com 的子域/Laravel API。我正在 DigitalOcean Apps 平台上完成所有这些工作。使用控制台,我可以手动运行 debug.js 文件,例如 node debug.js,它可以正确获取 URL/有效负载。但是,在 NextJS 内部,baseURL 被忽略,并且请求始终发送到 NextJS。

我不知道我还应该在这里做什么。因为 login 路线运行良好。我不确定是否是因为 config.headers.host 阻止了前往子域的请求。

非常感谢任何帮助。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文