Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

NextAuth not working behind corporate proxy #2509

Closed
raphaelpc opened this issue Aug 9, 2021 · 13 comments
Closed

NextAuth not working behind corporate proxy #2509

raphaelpc opened this issue Aug 9, 2021 · 13 comments
Labels
enhancement New feature or request

Comments

@raphaelpc
Copy link

Description 🐜

NextAuth is not working behind corporate proxy.

I studied the current NextAuth implementation and found that this error happens because NextAuth makes use of the "node-auth" library (npm package "oauth") in it's "oAuthClient" (src/server/lib/oauth/client.js), which requires manually setting up an agent to be used by the "https" library.

I created a PR with the solution of the problem:
#2493

Related discussion: #676
Related StackOverflow: https://stackoverflow.com/questions/32130471/node-js-https-not-working-behind-corporate-proxy

If the PR is not going to be accepted, i believe it is important do register the issue here for other people facing the same problems that i'm facing right now (which are quite time consuming to debug).

Is this a bug in your own project?

No

How to reproduce ☕️

Just run any application with NextAuth behind a corporate proxy and try to log in with a Provider (like Google or GitHub).

Screenshots / Logs 📽

[next-auth][error][oauth_get_access_token_error] 
https://next-auth.js.org/errors#oauth_get_access_token_error Error: connect ETIMEDOUT 216.58.202.141:443
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146:16) {
  errno: -4039,
  code: 'ETIMEDOUT',
  syscall: 'connect',
  address: '216.58.202.141',
  port: 443
} undefined undefined
[next-auth][error][oauth_get_access_token_error]
https://next-auth.js.org/errors#oauth_get_access_token_error Error: connect ETIMEDOUT 216.58.202.141:443
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146:16) {
  errno: -4039,
  code: 'ETIMEDOUT',
  syscall: 'connect',
  address: '216.58.202.141',
  port: 443
} google 4/0AX4XfWiDMdHUjXWR1xqLs8bGa1g5CEJOdBiypyNPA3b4vzJHeqxOGFownSkn5ABA885Asw
[next-auth][error][oauth_callback_error]
https://next-auth.js.org/errors#oauth_callback_error Error: connect ETIMEDOUT 216.58.202.141:443
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146:16) {
  errno: -4039,
  code: 'ETIMEDOUT',
  syscall: 'connect',
  address: '216.58.202.141',
  port: 443
}

Environment 🖥

System:
OS: Windows 10 10.0.19042
CPU: (12) x64 Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
Memory: 6.80 GB / 15.78 GB
Binaries:
Node: 14.16.1 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.5 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
npm: 6.14.12 - C:\Program Files\nodejs\npm.CMD
Browsers:
Edge: 44.19041.423.0
Internet Explorer: 11.0.19041.1
npmPackages:
react: ^17.0.2 => 17.0.2

Contributing 🙌🏽

Yes, I am willing to help solve this bug in a PR

@raphaelpc raphaelpc added the bug Something isn't working label Aug 9, 2021
@raphaelpc
Copy link
Author

raphaelpc commented Aug 9, 2021

To resolve this issue locally, i'm now using patch-package.

I'm applying this patch:

diff --git a/node_modules/next-auth/dist/server/lib/oauth/client.js b/node_modules/next-auth/dist/server/lib/oauth/client.js
index e35acd0..5b666ec 100644
--- a/node_modules/next-auth/dist/server/lib/oauth/client.js
+++ b/node_modules/next-auth/dist/server/lib/oauth/client.js
@@ -9,6 +9,10 @@ exports.default = oAuthClient;
 
 var _oauth = require("oauth");
 
+var UrlLib = require("url");
+
+var HttpsProxyAgent = require("https-proxy-agent");
+
 var _querystring = _interopRequireDefault(require("querystring"));
 
 var _logger = _interopRequireDefault(require("../../../lib/logger"));
@@ -150,6 +154,12 @@ async function getOAuth2AccessToken(code, provider, codeVerifier) {
   const postData = _querystring.default.stringify(params);
 
   return new Promise((resolve, reject) => {
+    let parsedUrl = UrlLib.parse(url, true);
+    if (parsedUrl.protocol == "https:" && process.env.http_proxy) {
+      let agent = new HttpsProxyAgent(process.env.http_proxy);
+      this.setAgent(agent);
+    }
+
     this._request('POST', url, headers, postData, null, (error, data, response) => {
       if (error) {
         _logger.default.error('OAUTH_GET_ACCESS_TOKEN_ERROR', error, data, response);
@@ -228,6 +238,12 @@ async function getOAuth2(provider, accessToken, results) {
   }
 
   return new Promise((resolve, reject) => {
+    let parsedUrl = UrlLib.parse(url, true);
+    if (parsedUrl.protocol == "https:" && process.env.http_proxy) {
+      let agent = new HttpsProxyAgent(process.env.http_proxy);
+      this.setAgent(agent);
+    }
+
     this._request(httpMethod, url, headers, null, accessToken, (error, profileData) => {
       if (error) {
         return reject(error);

It would be great if this issue could be resolved in future versions!

Thanks in advance.

@ndom91
Copy link
Member

ndom91 commented Sep 5, 2021

@raphaelpc we released a beta for the new NextAuth v4 version today which uses the newer openid-client instead of node-oauth. Unfortunately there is still no built-in support for proxying.

They use sindresorhus/got for http requests, and you can set a custom agent so it shouldn't be too difficult to write up a workaround like the one you detailed here in your patch-package diff.

Notes:

Thinking out loud here based off the info in the above links.. but maybe here (https://github.com/nextauthjs/next-auth/blob/beta/src/server/lib/oauth/client.js), adding something along these lines?

const { custom } = require('openid-client');
const { HttpsProxyAgent } = require('hpagent');

custom.setHttpOptionsDefaults({
  agent: {
    https: new HttpsProxyAgent({
	keepAlive: true,
	keepAliveMsecs: 1000,
	maxSockets: 256,
	maxFreeSockets: 256,
	scheduling: 'lifo',
	proxy: 'https://localhost:8080'
    })
  }
});

@ThePaulMcBride
Copy link

ThePaulMcBride commented Feb 9, 2022

I've had this same issue I think. I have a site (wecodeni.com) that uses next-auth.js. Every now and then I'll have a customer get in touch saying they can't sign up when using the email login. I can never work out exactly what is causing it, but it's always from larger corporate customers.

I can see from my database that a user is created in these cases, but the user is presented with the ?error=Verification error. Is there anything I can do to stop this from happening?

I'm pretty sure what's happening is their email server is scanning links in emails, and by requesting the link in the signin email, they are claiming it, and so the user can't then signin.

@ndom91
Copy link
Member

ndom91 commented Feb 9, 2022

@ThePaulMcBride looks like this might be a result of the invites being expired.

See:

const invalidInvite = !invite || invite.expires.valueOf() < Date.now()
if (invalidInvite) {
return { redirect: `${url}/error?error=Verification`, cookies }
}

EDIT: Sorry just reread your email. We had an issue like this before with Office365, for example, like you suggested - it'll "check" the link to make sure its not a phishing site or whatever, thereby "using" up the invitation link. Let me see if I can find the issue / discussion again.

There was a way to disable it in office365 and some other discussion.

EDIT 2: Yeah here it is! #1840

@raphaelpc
Copy link
Author

raphaelpc commented Feb 9, 2022 via email

@ndom91
Copy link
Member

ndom91 commented Feb 9, 2022

As mentioned in @balazsorban44's final reply in that issue, you could modify the [...nextauth].js config file (which is a "normal" catch-all API route at the end of the day), to just return a 200 immediately upon receiving a HEAD request.

This should satisfy most URL checking / SafeLink behavior, and not allow the email invitation tokens to be used up.

Something like:

import type { NextApiRequest, NextApiResponse } from "next"
import NextAuth from "next-auth"

export default async function auth(req: NextApiRequest, res: NextApiResponse) {

  if(req.method === "HEAD") {
     return res.status(200)
  }
  
  ...
}

See NextAuth.js - Advanced Initialization for more details, as well as the issue #1840 as previously mentioned.

@raphaelpc
Copy link
Author

raphaelpc commented Feb 10, 2022 via email

@raphaelpc
Copy link
Author

raphaelpc commented Feb 10, 2022 via email

@ThePaulMcBride
Copy link

Sorry for jumping on your thread Rapheal, I initially had thought we were seeing the same thing!

@ndom91
Copy link
Member

ndom91 commented Feb 10, 2022

@raphaelpc ah right, okay my bad. The suggestion I made/wrote a tutorial for in the docs was strictly for the issue of corporate email clients / email services sending HEAD requests to the Email invitation links, in affect "using them up" so that they are no longer valid once the user tries to click on them. This seems to eb @ThePaulMcBride's problem, right?

In terms of corporate proxy in general, we're now using openid-connect which uses node's built-in http and https modules to make requests (Source). And it doesn't look like those two modules support the common HTTP_PROXY and HTTPS_PROXY environment variables. (See: http docs, https docs).

Therefore, you'll have to either fork and modify openid-client to use an http client that supports these proxy environment variables out of the box (like axios or got). Or use some sort of global proxy work-around like global-tunnel. I've used global-tunnel before to setup a MITM proxy for an app, its super simple to use. Check out this stackoverflow post on using it to add proxy support to http / https: https://stackoverflow.com/a/25122807/9579094

With global-tunnel implemented, you can either set a corporate proxy in the JS setup method of global tunnel, or use the common HTTP_PROXY / HTTPS_PROXY environment variables.

@balazsorban44
Copy link
Member

balazsorban44 commented Feb 10, 2022

HEAD requests should not be a problem, see my comment: #3900 (review)

Regarding fixing with a patch, openid-clinet allows you to set your agent for requests: https://github.com/panva/node-openid-client/blob/main/docs/README.md#customizing-individual-http-requests if that is any helpful. We create the client here:

const client = new issuer.Client(

We let all openid-client settings pass through https://next-auth.js.org/configuration/providers/oauth#client-option but the agent setting is somewhat obscure (I think on purpose) and you can only modify it after the client has been created. So not sure if it would be possible without a change from us or patching.

@raphaelpc
Copy link
Author

HEAD requests should not be a problem, see my comment: #3900 (review)

Regarding fixing with a patch, openid-clinet allows you to set your agent for requests: https://github.com/panva/node-openid-client/blob/main/docs/README.md#customizing-individual-http-requests if that is any helpful. We create the client here:

const client = new issuer.Client(

We let all openid-client settings pass through https://next-auth.js.org/configuration/providers/oauth#client-option but the agent setting is somewhat obscure (I think on purpose) and you can only modify it after the client has been created. So not sure if it would be possible without a change from us or patching.

Thanks for the tip.

I have now been able to patch NextAuth v4 to be able to use it behind corporate proxy.

This is what i did:

  1. First, i tried to resolve the problem directly through NextAuth configuration, exploring this option that is already provided:

if (provider.httpOptions) custom.setHttpOptionsDefaults(provider.httpOptions)

To do so, i first updated my Provider at [...nextauth].ts:

    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      httpOptions: {
        agent: new HttpsProxyAgent(process.env.http_proxy),
      },
    }),

Doing like that it unfortunately didn't work. I got this error:

message: 'The "options.agent" property must be one of Agent-like Object, undefined, or false. Received an instance of Object'

Probably because the way the "agent" is being received by the openidClient function makes it stop being a Agent-like Object.

  1. To finally resolve the issue locally, i'm using patch-package.

I'm applying this patch:

diff --git a/node_modules/next-auth/core/lib/oauth/client.js b/node_modules/next-auth/core/lib/oauth/client.js
index 77161bd..1082fba 100644
--- a/node_modules/next-auth/core/lib/oauth/client.js
+++ b/node_modules/next-auth/core/lib/oauth/client.js
@@ -7,11 +7,19 @@ exports.openidClient = openidClient;
 
 var _openidClient = require("openid-client");
 
+var HttpsProxyAgent = require("https-proxy-agent");
+
 async function openidClient(options) {
   const provider = options.provider;
-  if (provider.httpOptions) _openidClient.custom.setHttpOptionsDefaults(provider.httpOptions);
-  let issuer;
+  let httpOptions = {};
+  if (provider.httpOptions) httpOptions = { ...provider.httpOptions };
+  if (process.env.http_proxy) {
+    let agent = new HttpsProxyAgent(process.env.http_proxy);
+    httpOptions.agent = agent;
+  }
+  _openidClient.custom.setHttpOptionsDefaults(httpOptions);
 
+  let issuer;
   if (provider.wellKnown) {
     issuer = await _openidClient.Issuer.discover(provider.wellKnown);
   } else {

It would be great if this issue could be resolved in future versions!
Maybe with an option like "useProxyAgent" that, if true, the openidClient function will check that a "process.env.http_proxy" exists and, if so, sets an HttpsProxyAgent like in my patch?

Anyway, in case anyone else has the same problem as i, i hope the patch can help! :)

Thanks!

@balazsorban44 balazsorban44 added enhancement New feature or request and removed bug Something isn't working labels Feb 10, 2022
@balazsorban44
Copy link
Member

Yeah. Unfortunately, this is not a priority right now as the use case seems to be uncommon enough, but I'm happy to keep this issue open as long as it doesn't get stale.

@nextauthjs nextauthjs locked and limited conversation to collaborators Feb 12, 2022
@balazsorban44 balazsorban44 converted this issue into discussion #3944 Feb 12, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants