-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(client): improved basepath detection #2320
feat(client): improved basepath detection #2320
Conversation
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/nextauthjs/next-auth/24v71m2c6rkNcs5xN5bYyb3Ttwih |
Codecov Report
@@ Coverage Diff @@
## next #2320 +/- ##
==========================================
+ Coverage 10.74% 10.86% +0.11%
==========================================
Files 83 83
Lines 1414 1427 +13
Branches 393 402 +9
==========================================
+ Hits 152 155 +3
- Misses 1042 1050 +8
- Partials 220 222 +2
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome 💯 ✨ , I think we should think this twice, whether we really want to introduce another environment variable on the setup or not 👍🏽
I left some cleanup suggestions as well 💬
jest.resetModules() // Most important - it clears the cache | ||
process.env = { ...OLD_ENV } // Make a copy | ||
}) | ||
|
||
afterAll(() => { | ||
process.env = OLD_ENV // Restore old environment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's no need for comments here, the code is pretty self-descriptive 👍🏽
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense.
|
||
if (!url) { | ||
if (process.env.NEXT_PUBLIC_NEXTAUTH_URL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the impression we should move this to v4 and change NEXTAUTH_URL
to be NEXT_PUBLIC_NEXTAUTH_URL
. In this way, we can keep a single environment variable and prevent code complexity by having to manage two of them. What do you think @balazsorban44 ? 🧶
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is intended to be part of v4, otherwise it would be a big breaking change, IMO. I made the pull request against the next
branch.
I like the idea of having a single variable.
@@ -5,23 +5,36 @@ | |||
* supporting a default value, so a simple split is sufficent. | |||
* @param {string} url | |||
*/ | |||
export default function parseUrl (url) { | |||
export default function parseUrl(url) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is also called server-side, and the logic currently works because there we pass it NEXTAUTH_URL
.
I think we should move that logic inside, hence whenever calling parseUrl
it can automatically compute what the right base URL and base path are:
export default function parseBaseUrl(url) {
// ..
if (process.env.NEXTAUTH_URL) {
if (!process.env.NEXT_PUBLIC_NEXTAUTH_URL) {
throw new Error("[next-auth] Trying to use a custom `NEXTAUTH_URL` without setting `NEXT_PUBLIC_NEXTAUTH_URL`, client-side redirects won't work")
Now that we're modifying this function, I think we should:
- rename it as it's not generic utility about URLs but is intended to compute the base URL
throw
in case expectations don't match so we're explicit about mis-configurations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should move that logic inside, hence whenever calling parseUrl it can automatically compute what the right base URL and base path are
Yes, currently, it feels like it's dangling between both client and server.
throw in case expectations don't match so we're explicit about mis-configurations
Agreed.
if ( | ||
process.env.NODE_ENV === "development" && | ||
!/^http:\/\/localhost:\d+$/.test(process.env.NEXTAUTH_URL) && | ||
!process.env.NEXT_PUBLIC_NEXTAUTH_URL | ||
) { | ||
logger.warn( | ||
"NEXT_PUBLIC_NEXTAUTH_URL", | ||
`NEXTAUTH_URL is set to "${process.env.NEXTAUTH_URL}" instead of the default "http://localhost:\${PORT}", and NEXT_PUBLIC_NEXTAUTH_URL is not set. Client side path detections will fail.` | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, we should move this logic to parseUrl
and the check above too to start sliming down this monolith file.
On the other hand, we should be throwing errors in both cases rather than warnings as this misconfiguration, I understand, is critical? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the other hand, we should be throwing errors in both cases rather than warnings as this misconfiguration, I understand, is critical?
I agree on this. I could not think of a better message, so I left it as warning, but it should definitely be treated as error.
url = "http://localhost:3000/api/v1/authentication/" | ||
// this semi-colon is needed, otherwise js thinks the string | ||
// above is a function | ||
;({ baseUrl, basePath } = parseUrl(url)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand we don't need to re-assign url
here, why not just do:
const url1 = "http://localhost:3000"
const { baseUrl: baseUrl1, basePath: basePath1 } = parseUrl(url1)
// ...
const url2 = "http://localhost:3000"
const { baseUrl: baseUrl2, basePath: basePath2 } = parseUrl(url2)
I think will make this case a bit nicer to read 👍🏽
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to do this but decided against it. But it seems like it would actually be better this way.
|
||
expect(baseUrl).toBe("https://www.mydomain.com") | ||
expect(basePath).toBe("/api/v3/auth") | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably want to add test cases for the unhappy paths as well 🙏🏽
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. That would probably make more sense once we have refactored parseUrl() to throw errors.
@@ -56,7 +56,7 @@ export function useSession(options = {}) { | |||
|
|||
React.useEffect(() => { | |||
if (requiredAndNotLoading) { | |||
const url = `/api/auth/signin?${new URLSearchParams({ | |||
const url = `${_apiBaseUrl()}/signin?${new URLSearchParams({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could use parseUrl()
rather than having duplicate utils?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure on this one.
As far as I understand, _apiBaseUrl()
checks the __NEXTAUTH
object, which in turn makes use of the parseUrl()
function. So technically it could be refactored.
I would leave it on the core team to make decisions on this and refactor accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I didn't realise _apiBaseUrl()
calls parseUrl()
internally, as you mention worth of a refactor in a different PR 👍🏽
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mahieyin-rahmun could you move this change to a different PR please? 🙏🏽
updated as per review request Co-authored-by: Lluis Agusti <[email protected]>
const protocol = url.startsWith("http:") ? "http" : "https" | ||
|
||
// Normalize URLs by stripping protocol and no trailing slash | ||
url = url.replace(/^https?:\/\//, '').replace(/\/$/, '') | ||
url = url.replace(/^https?:\/\//, "").replace(/\/$/, "") | ||
|
||
// Simple split based on first / | ||
const [_host, ..._path] = url.split('/') | ||
const [_host, ..._path] = url.split("/") | ||
const baseUrl = _host ? `${protocol}://${_host}` : defaultHost | ||
const basePath = _path.length > 0 ? `/${_path.join('/')}` : defaultPath | ||
const basePath = _path.length > 0 ? `/${_path.join("/")}` : defaultPath |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, is this something that could be accomplished using URL
?
Something like:
let baseUrl = defaultHost;
let basePath = defaultPath;
try {
url = new URL(url, url.startsWith('http') ? undefined : defaultHost);
baseUrl = url.origin;
basePath = url.pathname.length ? url.pathname : defaultPath;
} catch (e) {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting PoV. I will try and test this out. Looks like this could work, with some modifications.
I haven't had a closer look yet, but I would definitely keep a single env variable, not introduce a new one. adding Another way of inlining (making it available client-side) an env variable is to use the I think only a very few of our users will actually need this change at all. Most of the time I can be convinced otherwise, but I don't think we should keep both variables either way.
Forget it. We have more logic in the server-side that relies on this variable. (Eg.: redirects) |
I agree about the one variable part, and I understand about it being a big change, but I would argue that with the amount of breaking changes in v4 already, people would have to shift around bits of their codebase anyway. An environment variable, IMHO, should not be that big of a deal. Those who don't want to, can stick with v3, which seems like a reasonable tradeoff. And most new users would start off with v4 anyway.
Yes, they introduced
IMHO, there are quite a few issues that talk about this, and although it probably is a small portion of the userbase, it would be a good addition, since it keeps everything dynamic as opposed to hardcoded or working on assumptions. |
@mahieyin-rahmun I've been speaking about this with @balazsorban44, we think adding We think it's something we could add as a feature in a later v4 release, like Meanwhile, you can use one of the two solutions mentioned here:
that would get you going 👍🏽 |
@mahieyin-rahmun, we're gonna close this 👍🏽 , but we keep an eye on it 👀 for when we add this back on |
@lluia, Sure, let me know when it is time, I am willing to open another PR with the suggested refactors/modifications in this PR. |
Reasoning 💡
This PR attempts to improve client side path detection by leveraging a second environment variable named
NEXT_PUBLIC_NEXTAUTH_URL
to ensure client side path detections are always consistent, regardless of folder structure and URL.Currently, there are some issues with the client side path detection. e.g. signIn() function assumes that the folder structure is like the following:
and
NEXTAUTH_URL
to be set tohttp://localhost:3000
orhttps://mydomain.com
. The above breaks if the URL and/or folder structure of the user's project is different.With these changes, users will be able to use a different folder structure if they want, such as:
And set
NEXTAUTH_URL
to something likehttp://localhost:3000/api/v1/authentication
. As compensation for the flexibility, they now need to declare a second environment variable:Luckily, this can be simplified by using variables expansion feature of Next.js
Checklist 🧢
Affected issues 🎟
Closes #1713, #2316. Related to #689, #1712, #1517, #900, #499, #1676