nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.05k stars 3.33k forks source link

proxy, google oauth, and connection refusal #1366

Closed mdelapenya closed 3 years ago

mdelapenya commented 3 years ago

Describe the bug NextAuth is not handling the session when the NextJS app is behind a proxy.

Steps to reproduce

  1. Clone https://github.com/nextauthjs/next-auth-example
  2. Add the following Dockerfile to the cloned repo:
FROM node:alpine

RUN mkdir -p /usr/src
WORKDIR /usr/src

# Copy package.json and package-lock.json before other files
# Utilise Docker cache to save re-installing dependencies if unchanged
COPY ./package*.json ./

# Install dependencies
RUN npm install --production

COPY . /usr/src

RUN npx next telemetry disable
RUN npm run build
EXPOSE 3000
ENTRYPOINT [ "npm", "run", "start" ]
  1. Add the following docker-compose.yml file next to the Dockerfile, which uses https://github.com/bunkerity/bunkerized-nginx as a web proxy to handle HTTPS:
---
version: '3'
services:
  proxy:
    image: bunkerity/bunkerized-nginx:1.2.1
    container_name: proxy
    environment:
      - AUTO_LETS_ENCRYPT=no
      - CONTENT_SECURITY_POLICY=default-src https://local.myapp.com https://fonts.googleapis.com https://fonts.gstatic.com https://*.googleusercontent.com https://platform-lookaside.fbsbx.com https://js.stripe.com 'self' 'unsafe-eval' 'unsafe-inline' ; frame-ancestors 'self'; form-action https://accounts.google.com https://*.facebook.com 'self' ; block-all-mixed-content; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';
      - GENERATE_SELF_SIGNED_SSL=yes
      - ALLOWED_METHODS=DELETE|GET|OPTIONS|PATCH|POST|PUT
      - BLOCK_ABUSERS=yes
      - BLOCK_PROXIES=yes
      - BLOCK_TOR_EXIT_NODE=yes
      - BLOCK_USER_AGENT=yes
      - NEXTAUTH_URL=https://local.myapp.com
      - REDIRECT_HTTP_TO_HTTPS=yes
      - USE_REVERSE_PROXY=yes
      - REVERSE_PROXY_URL_1=/
      - REVERSE_PROXY_HOST_1=http://web:3000
      - REVERSE_PROXY_URL_2=/api/auth
      - REVERSE_PROXY_HOST_2=http://web:3000/api/auth
      - SERVE_FILES=no
      - SERVER_NAME=local.myapp.com
      - USE_BROTLI=yes
      - USE_GZIP=yes
      - USE_MODSECURITY=no
      - USE_PROXY_CACHE=yes
    healthcheck:
      test: ["CMD-SHELL", "wget -O /dev/null http://localhost:8080 || exit 1"]
      timeout: 10s
    volumes:
      - ./nginx/letsencrypt:/etc/letsencrypt
    ports:
      - 80:8080
      - 443:8443
    depends_on:
      - web
    networks:
      - nextauth
    restart: always
  web:
    build: "."
    container_name: "web"
    environment:
      - NEXTAUTH_SITE=https://local.myapp.com
      - NEXTAUTH_URL=https://local.myapp.com
      - GOOGLE_ID=YOUR_GOOGLE_IS
      - GOOGLE_SECRET=YOUR_GOOGLE_SECRET
    networks:
      - nextauth
    restart: always

networks:
  nextauth:
  1. Make sure you update your Google ID and secret in the docker-compose file
  2. Make sure you add https://local.myapp.com/api/auth/callback/google to the Authorised URLs in the OAuth credentials section in your Google Cloud console
  3. Add local.domain.example to your /etc/hosts: 127.0.0.1 local.domain.example
  4. Make sure there is nothing running at 80 or 443 ports
  5. Start the project with docker-compose up
  6. Navigate to https://local.myapp.com (Note: in Chrome you maybe need to type "thisisunsafe")
  7. Login with your Google account. It will work
  8. Logout
  9. Login again with same or another account

Expected behavior You should be able to login again

Current behavior It's not possible to login again.

Screenshots or error logs If applicable add screenshots or error logs to help explain the problem.

Additional context Verified in Chrome and Firefox, and there is an open discussion #676 with more context.

There is a weird behaviour here: when the proxy is recreated:

docker-compose stop proxy && docker-compose kill proxy && docker-compose up -d proxy

and you browse local.myapp.com in another tab (so Chrome requires you to type again the 'thisisunsafe' thing) it is possible to login again, and it also could be the case that if the session is still active, you could see yourself logged in. But at the moment you logout, then it's not possible again.

Feedback Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.

Not sure if this is the right manner to do, but wanted to put attention in the https://github.com/nextauthjs/next-auth/discussions/676 discussion, where we explained an use case where auth is not able to go via proxy, including an example.

Any help there?

mdelapenya commented 3 years ago

Hey @balazsorban44 @iaincollins I updated the issue following the template with repro steps. Could you please ๐Ÿ‘€ ๐Ÿ™ ?

I'm not a frontend engineer, but my first guess is related to using native fetch when a web proxy is sitting in front of the NextJS app. Not sure if using https://www.npmjs.com/package/node-fetch-with-proxy or node-https-proxy-agent could help here (more info on the latter: https://github.com/node-fetch/node-fetch/issues/79#issuecomment-184594701)

Thank you in advance for this wonderful library, it is awesome!

image

balazsorban44 commented 3 years ago

Would https://next-auth.js.org/configuration/options#nextauth_url_internal help here?

mdelapenya commented 3 years ago

Will take a look, as that variable was not present when I started this thread back in January, thanks!! ๐Ÿ˜ƒ

mdelapenya commented 3 years ago

No, it is not working :(

Adding more context: clicking in the "Server" link in the example menu throws this log error in the server:

web | [next-auth][error][client_fetch_error] web | https://next-auth.js.org/errors#client_fetch_error session FetchError: request to https://local.myapp.com/api/auth/session failed, reason: connect ECONNREFUSED 127.0.0.1:443 web | at ClientRequest. (/usr/src/node_modules/node-fetch/lib/index.js:1461:11) web | at ClientRequest.emit (node:events:378:20) web | at TLSSocket.socketErrorListener (node:_http_client:462:9) web | at TLSSocket.emit (node:events:378:20) web | at emitErrorNT (node:internal/streams/destroy:188:8) web | at emitErrorCloseNT (node:internal/streams/destroy:153:3) web | at processTicksAndRejections (node:internal/process/task_queues:81:21) { web | type: 'system', web | errno: 'ECONNREFUSED', web | code: 'ECONNREFUSED' web | } proxy | 172.19.0.1 - - local.myapp.com [29/Mar/2021:05:49:56 +0000] "GET /_next/data/6w9DobGzr43yUxUwLlOHW/server.json HTTP/2.0" 200 45 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36"

mdelapenya commented 3 years ago

wait... After updating the NEXTAUTH_URL_INTERNAL to https://proxy:8443, which is proxy's internal URL, and adding NODE_TLS_REJECT_UNAUTHORIZED=0 (to skip verification of the self-signed certificate), then the error is different:

web | [next-auth][error][client_fetch_error] web | https://next-auth.js.org/errors#client_fetch_error session FetchError: invalid json response body at https://proxy:8443/api/auth/session reason: Unexpected token < in JSON at position 0 web | at /usr/src/node_modules/node-fetch/lib/index.js:272:32 web | at processTicksAndRejections (node:internal/process/task_queues:94:5) { web | type: 'invalid-json' web | } proxy | 172.19.0.2 - - proxy [29/Mar/2021:06:06:42 +0000] "GET /api/auth/session HTTP/1.1" 403 146 "-" "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)"

Maybe similar to https://github.com/realm/realm-js/pull/1556/files?

mdelapenya commented 3 years ago

On the other hand, using NEXTAUTH_URL_INTERNAL=http://proxy:8080 for insecure communications from proxy to webapp, it gives the following fetch error, although instead of Google APIs returning a 403 Forbidden error (as above), it returns a 302 Found (see last log error)

web | [next-auth][error][client_fetch_error] web | https://next-auth.js.org/errors#client_fetch_error session FetchError: request to https://proxy/api/auth/session failed, reason: connect ECONNREFUSED 172.21.0.3:443 web | at ClientRequest. (/usr/src/node_modules/node-fetch/lib/index.js:1461:11) web | at ClientRequest.emit (node:events:378:20) web | at TLSSocket.socketErrorListener (node:_http_client:462:9) web | at TLSSocket.emit (node:events:378:20) web | at emitErrorNT (node:internal/streams/destroy:188:8) web | at emitErrorCloseNT (node:internal/streams/destroy:153:3) web | at processTicksAndRejections (node:internal/process/task_queues:81:21) { web | type: 'system', web | errno: 'ECONNREFUSED', web | code: 'ECONNREFUSED' web | } proxy | 172.21.0.1 - - local.myapp.com [29/Mar/2021:06:15:47 +0000] "GET /api/auth/callback/google?state=REDACTED&code=REDACTED&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&authuser=1&prompt=none HTTP/2.0" 302 0 "https://accounts.google.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36"

Please take a look at the IP in the error: fetch still thinks HTTPS is handled by the web service (ECONNREFUSED 172.21.0.3:443), as the proxy service runs on 127.21.0.1

mdelapenya commented 3 years ago

@balazsorban44 any guess about the behaviour shown in this issue? ๐Ÿ™

pesickadavid commented 3 years ago

@mdelapenya Hi! Did you manage to solve this problem? Thanks.

mdelapenya commented 3 years ago

Nope, still stuck on that. Did not have time to work on that, but thinking about replacing node-fetch using the proxy version in my fork

stale[bot] commented 3 years ago

Hi there! It looks like this issue hasn't had any activity for a while. It will be closed if no further activity occurs. If you think your issue is still relevant, feel free to comment on it to keep it open. (Read more at #912) Thanks!

stale[bot] commented 3 years ago

Hi there! It looks like this issue hasn't had any activity for a while. To keep things tidy, I am going to close this issue for now. If you think your issue is still relevant, just leave a comment and I will reopen it. (Read more at #912) Thanks!

kidp2h commented 1 year ago

Would https://next-auth.js.org/configuration/options#nextauth_url_internal help here?

this is working with nginx proxy, thx u