Skip to content

Prefer User-Defined Host (NEXTAUTH_URL) Over Forwarded Host in Vercel#4509

Closed
jjorissen52 wants to merge 1 commit into
nextauthjs:mainfrom
jjorissen52:fix/default-nextauth-url
Closed

Prefer User-Defined Host (NEXTAUTH_URL) Over Forwarded Host in Vercel#4509
jjorissen52 wants to merge 1 commit into
nextauthjs:mainfrom
jjorissen52:fix/default-nextauth-url

Conversation

@jjorissen52

@jjorissen52 jjorissen52 commented May 5, 2022

Copy link
Copy Markdown

This PR affects behavior that can only be observed in Vercel deployments.

The commit reverts behavior introduced by #3649, where the user-defined environment variable NEXTAUTH_URL is ignored in favor of the value of x-forwarded-host.

☕️ Reasoning

If a user deliberately sets a NEXTAUTH_URL environment variable, it means they wanted Vercel to use this as the canonical URL. next-auth should consider this deliberately defined value first and then fallback to automatic behavior otherwise. The current behavior, which is to use x-forwarded-host when available and fallback to user-defined NEXTAUTH_URL (on top of being surprising and not consistent with the documentation) breaks in multiple situations, including our current staging environment:

  1. If a proxy sits between the user and the deployment on Vercel, wherein the actual site host and Vercel's deployment URL do no match, next-auth will end up using Vercel's deployment URL. For example, we have nginx listening at example.dev and our staging deployment at staging.example.com. Even though NEXTAUTH_URL=example.dev, next-auth sets redirect_uri to staging.example.com.
  2. If you are using a Preview deployment, e.g. example.vercel.app, the current behavior is to set redirect_uri=<preview_url> which is practically guaranteed to not be whitelisted by your provider (since you can't know the auto-generated preview URL ahead of time).

🧢 Checklist

  • Documentation consistent with existing
  • Tests
  • Ready to be merged (behavior tested locally)

🎫 Affected issues

Fixes: #4507

📌 Resources

@vercel

vercel Bot commented May 5, 2022

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Ignored Deployment
Name Status Preview Updated
next-auth ⬜️ Ignored (Inspect) May 5, 2022 at 6:18PM (UTC)

@github-actions github-actions Bot added the core Refers to `@auth/core` label May 5, 2022
@Naddiseo

Naddiseo commented May 5, 2022

Copy link
Copy Markdown
Contributor

There is a tangent situation, which I didn't mention in my original bug report: if there really is a proxy.

We have two apps that connect to the same backend api (where [...nextauth].ts is located), and access it using nextjs's rewrite on the two apps, which behaves as a proxy and has the x-forwarded-host header.

///next.config.js in app1 and app2
module.exports = {
   rewrites() {
      return [
          {source: "/api/:path*", destination: `${process.env.API_URL}/api/:path*` }
      ]
   }
};

Our architecture is like this:

+-------------------------+     +--------------------------------+
| app1.com/api/auth       | ->  |     api.backend.com/api/auth   | 
+-------------------------+     |                                |
                                |                                |
+-------------------------+     |                                |
|  app2.com/api/auth      |  -> |                                |
+-------------------------+     +--------------------------------+
 

So for each request, we need to know which of the two apps the request came from so that next-auth will redirect back to the correct one. In this particular case, the x-forward-host header is actually very useful, except when the request comes from an oauth callback.
I currently have to detect the x-forwarded-host header AND the next-auth.callback-url cookie THEN set NEXTAUTH_URL explicitly.

If this PR were changed slightly to pull the pathname from NEXTAUTH_URL if it exists, and also took the callback url in cookie into account, next-auth's long standing request for a dynamic NEXTAUTH_URL would be satisfied

@jjorissen52

jjorissen52 commented May 5, 2022

Copy link
Copy Markdown
Author

@Naddiseo Just reading through it, I think the callback url in that cookie is set based on the return value of detectHost. It's unclear to me what changes would be required to accomplish what you are trying to do in a way that doesn't break our use case, which is to proxy the entire Vercel deployment. One way or another, the callbackUrl will either need be set from NEXTAUTH_URL or x-forwarded-host.

In your case, you want it to be set by x-forwarded-host, because you want it to return your user to the same app URL they arrived on. In our case, x-forwarded-host refers to a URL that we never want the user to directly visit (which is the point of the NEXTAUTH_URL according to the documentation). It's unclear to me how we resolve those two goals which are in conflict with this PR.

@Naddiseo

Naddiseo commented May 6, 2022

Copy link
Copy Markdown
Contributor

@jjorissen52, I think I understand what you're saying: In a non-proxy environment the x-forwarded-host refers to the deployment url instead of the assigned domain? In which case, I see what you're saying about my use case being at odds.

I think I may have originally misunderstood how/what the x-forward-host actually was, because how it works, works in our favour if it also picked up the custom pathname something like:

if (process.env.VERCEL) {
    if (process.env.NEXTAUTH_ENV) { // http://example.com/custom/api/auth/path
        const {pathname} = new URL(process.env.NEXTAUTH_ENV);
        return `${forwardedHost}${pathname}`; // forwarded.com/custom/api/auth/path
    }
    return forwardedHost
}

Given that, I do think this PR is the correct fix in that it makes the code match what the documentation says it'll do.

For reference, how we're solving the multi app issue is something like:

/// [...nextauth].ts
export default (req,res) =>{ 
    const forwardedHost = req.headers['x-forwarded-host'];
    const callbackUrl = req.cookies['next-auth.callback-url'];
    const baseUrl = isAllowedUrl(forwardedHost)
        ? forwardedHost 
        : isAllowedUrl(callbackUrl) 
            ? callbackUrl
            : undefined;
    assert(typeof baseUrl !== "undefined");
    const url = new Url(baseUrl);
    url.pathname = "/custom/api/auth";
    
    process.env.NEXTAUTH_URL = url.toString();

    
    return NextAuth(req,res, { ...options});
}

@jjorissen52

Copy link
Copy Markdown
Author

Yes, you seem to understand the issue now. And that code snippet is very interesting. We hadn't considered making modifications like yours. We might be able to do something similar...

@jjorissen52

jjorissen52 commented May 6, 2022

Copy link
Copy Markdown
Author

@Naddiseo We haven't had a chance to completely verify that this fixes our problem (I will come back with an update) but it looks like it works in the preview deployment this fixes the problem for us:

export default (req, res) => {
  /* next-auth prioritizes x-forwarded-host over NEXTAUTH_URL
    when running in Vercel https://github.com/nextauthjs/next-auth/pull/4509 .
    We need that to not happen, since the actual domain from *our* nginx proxy will differ
    from x-forwarded-host set by Vercel's proxy. E.g.:
      1. Our Nginx listening on example.dev proxies staging.example.com
      2. Vercel's Nginx listening at staging.example.com sets x-forwarded-host to staging.example.com
      3. next-auth will now set redirect_uri to staging.example.com instead of example.dev
  */
  if (process.env.VERCEL) {
    // prefer NEXTAUTH_URL, fallback to x-forwarded-host
    req.headers["x-forwarded-host"] = process.env.NEXTAUTH_URL || req.headers["x-forwarded-host"]
  }
  return NextAuth(req, res, options) // eslint-disable-line new-cap
}

Since x-forwarded-host is prioritized by next-auth in detectHost, you can control the callback URL by setting x-forwarded-host manually.

@stale

stale Bot commented Jul 6, 2022

Copy link
Copy Markdown

It looks like this issue did not receive any activity for 60 days. It will be closed in 7 days if no further activity occurs. If you think your issue is still relevant, commenting will keep it open. Thanks!

@stale stale Bot added the stale Did not receive any activity for 60 days label Jul 6, 2022
@stale

stale Bot commented Jul 21, 2022

Copy link
Copy Markdown

To keep things tidy, we are closing this issue for now. If you think your issue is still relevant, leave a comment and we might reopen it. Thanks!

@stale stale Bot closed this Jul 21, 2022
@jjorissen52

Copy link
Copy Markdown
Author

Please re-open this, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Refers to `@auth/core` stale Did not receive any activity for 60 days

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pathname of NEXTAUTH_URL is ignored in when deployed to vercel

3 participants