Closed warflash closed 6 months ago
I absolutely agree with you, it is quite wasteful to make full-blown http requests in Node side. What I don't understand, however, is that we are already using $fetch
to avoid that:
https://github.com/sidebase/nuxt-auth/blob/adfe97ee22890934acb3e179651ef72bc4ade6dd/src/runtime/utils/fetch.ts#L5-L24
I guess we'd have to refer to the $fetch
docs and probably check our passed options. Will look into it later (possibly this week)
Awesome, thank you @phoenix-ru!
While looking into this I came up with two possible reasons. Please do note however that that's just possible explanations I had, not based on much. But figured sharing can't hurt.
Those calls are made by authjs itself somewhere internally and are not using $fetch
Prepending the auth.baseURL
prop to fetch calls makes $fetch
unable to resolve the fact that the calls are local. With a full featured domain it has no way of knowing those are supposed to be internal calls. If that's the case then the easiest solution might be to have a auth.baseUrl
as well as auth.serverBaseUrl
that's used in import.meta.server contexts.
So after looking into this it turns out the fetch implementation does indeed prepend the entire host section of the URL. This causes $fetch
to not resolve calls internally.
I've played with it for a bit and technically all that's needed is a change in the fetch constructor. -> https://github.com/warflash/nuxt-auth-debug/commit/746cfd8f1c8b5262f301f6278f012fb497163a5f
To show the differences and debug I went ahead and created cpu flamegraphs for both versions.
Notice the fetch call and http dispatch.
Full cpuprofile: no-local-fetch.cpuprofile
Notice the localFetch working and only calling callHandle
Full cpuprofile: local-fetch-ufo.cpuprofile
Obviously on localhost those changes only shave off .2ms, however in a real env they save an entire roundtrip that would otherwise have been routed out to the public internet and back to the nuxt app.
Hi @warflash, thank you a lot for a thorough investigation, I can indeed confirm the issue exists. Moreover, it is caused by this particular line in Nuxt's $fetch
implementation (tldr request must start with /
):
https://github.com/nuxt/nuxt/blob/572c36745597ee45e9b57f8f32a24dd99a5b9d42/packages/nuxt/src/app/composables/fetch.ts#L172
I checked your linked repo, however, I don't think I can use it, since the issue is really in _fetch
implementation I already linked here.
It uses joinedPath
, which is a concatenation of baseURL
+ path
:
https://github.com/sidebase/nuxt-auth/blob/949e556f0ed71ea273e20f236b80748831e8b338/src/runtime/utils/url.ts#L8
baseURL
comes from computed state:
https://github.com/sidebase/nuxt-auth/blob/949e556f0ed71ea273e20f236b80748831e8b338/src/runtime/composables/commonAuthState.ts#L35-L38
and, finally, computed state fullBaseUrl
comes from user's baseURL
:
https://github.com/sidebase/nuxt-auth/blob/949e556f0ed71ea273e20f236b80748831e8b338/src/module.ts#L117-L119
https://github.com/sidebase/nuxt-auth/blob/949e556f0ed71ea273e20f236b80748831e8b338/src/module.ts#L128
So, going back to the $fetch
, it is always being called with http://localhost:3000/api/auth/...
, which triggers the usual HTTP fetch rather than a server procedure call.
The correct way would be to determine if the request is actually local or not. I would assume using baseURL
from ofetch
and selectively providing/omitting it.
But there may be much better solutions, so all the ideas are welcome.
I have seen quite a lot of confusion when it comes to origin, baseURL, pathname and other related things:
Even though there was origin
prior to v0.6, I think the whole design needs to be adjusted
Oh I apologize, for some reason my linked commit was faulty and didn't include the actual "fix"/workaround. Added that here: https://github.com/warflash/nuxt-auth-debug/commit/746cfd8f1c8b5262f301f6278f012fb497163a5f
But yeah, $fetch
indeed only works if it's a relative path that's passed into it. Prepending the origin breaks that behavior.
And I highly agree with your concerns regarding baseURL
and origin
and pathname
and whatnot. That combined with the fact that there's also a bunch of env vars floating around can get very tricky.
In this context and in the context of the workaround I linked I'd also like to point out that not leveraging nuxt's app.baseURL
seems like a weird design decision to me.
I feel like it should for sure be included somewhere in the path building logic -> https://github.com/sidebase/nuxt-auth/issues/340
Just from an minimal implementation perspective I feel like this logic: https://github.com/warflash/nuxt-auth-debug/commit/746cfd8f1c8b5262f301f6278f012fb497163a5f should already work for most things. In fact we've been using a patched version with that logic for 2 weeks now on our clusters and everything is smooth so far.
It still requires the possibility the overwrite the hardcoded /api/auth
path if users set their api in a different path + a way to set the auth domain, not for fetching but for next auth to validate redirects iirc.
Okay, I see it now:
NEXTAUTH_URL=https://example.com/custom-route/api/auth
;$fetch
can work with fully-specified URLs, but should do this only for external requests;$fetch
for internal functionality should always receive pathnames instead of full URLs;_fetch
is used in both authjs
and local
providers;@warflash Regarding app.baseURL
: I agree that it needs to be taken into account, however, the patch is not fully sufficient. I will try to come up with a patch which fits all 4 points above.
@warflash wow, thanks for reporting this issue! I can already see 50% improvement on req/sec when stress-tested on a local machine (and similar flamegraph results as yours). I am committing the patch, would you be able to test it on your loads and whether it satisfies your usecases?
Before:
After:
Great you're seeing the same sort of improvements, nice!
I am committing the patch, would you be able to test it on your loads and whether it satisfies your usecases?
Would there be a way for me to install a prerelease of that PR or should I go ahead and create a pnpm patch myself?
Would there be a way for me to install a prerelease of that PR
I will ask @zoey-kaiser to do a prerelease as a part of her functional review.
I will ask @zoey-kaiser to do a prerelease as a part of her functional review.
I will do it right now! ❤️
Just released the changes in a next
release tag: https://www.npmjs.com/package/@sidebase/nuxt-auth/v/0.8.0-alpha.1
We will also begin testing the improvements internally and include it in the next full 0.8.0 release! Thank you so much for bringing this issue to our attention and the amazing investigation! We really appreciate it ❤️
@zoey-kaiser I'm currently testing upgrade from 0.7.2 to 0.8.0-rc
This break my config : local provider and auth.baseURL defined to a custom base url ex : https://example.com/
Login is posted to the nuxt base URL , not to my custom base url
I have to redefine all the endpoints and removing the / prefix . Perhaps it has to become the default values ?
endpoints: {
signIn: { path: 'login', method: 'post' },
signOut: { path: 'logout', method: 'post' },
signUp: { path: 'register', method: 'post' },
getSession: { path: 'session', method: 'get' }
}
Hi @spras 👋
Could you please open a new issue for this! I will look into it tomorrow! 😊
Environment
Build Modules: -
Reproduction
https://github.com/warflash/nuxt-auth-debug
Using the module's
playground-authjs
dir.Describe the bug
In our production env we noticed a ton of
/api/auth/session?callbackUrl=http:%2F%2Flocalhost:3333%2Falways-unprotected
requests coming through our proxy layer, all with user agent set to "node".To debug this I spun up a local version of the playground of the repo and attached a proxy, -> see repro above.
I am not logged in on the local playground, depite that:
In the logs below you can see me calling for
/always-unprotected
with my browsers user agent in the first line. Then 3 requests are fired for/api/auth/session?callbackUrl=http:%2F%2Flocalhost:3333%2Falways-unprotected
(which is what we've been seeing in our prod proxy logs as well, followed by/api/auth/providers
and/api/auth/csrf
.Additional context
Wanted to perhaps spark a discussion or question on whether these calls can be avoided or made internally using $fetch or at least limited to users that are actually logged in or pages that are protected. Sending out a full http request for all of these adds a considerable amount of overhead and delay to the rendered SSR page.
Logs