nextauthjs / next-auth

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

NEXTAUTH_URL_INTERNAL variable does not work with keycloak #4006

Closed crisgarlez closed 1 year ago

crisgarlez commented 2 years ago

Provider type

Keycloak

Environment

System: OS: Windows 10 10.0.19043 CPU: (8) x64 Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz Memory: 3.30 GB / 15.76 GB Binaries: Node: 16.13.1 - C:\laragon\bin\nodejs\node-v16.13.1\node.EXE Yarn: 1.22.11 - ~\AppData\Roaming\npm\yarn.CMD npm: 8.3.2 - C:\laragon\bin\nodejs\node-v16.13.1\npm.CMD Browsers: Edge: Spartan (44.19041.1266.0), Chromium (98.0.1108.55) Internet Explorer: 11.0.19041.1202 npmPackages: next: 12.0.7 => 12.0.7 next-auth: ^4.0.6 => 4.0.6 react: 17.0.2 => 17.0.2

Reproduction URL

https://github.com/nextauthjs/next-auth-example

Describe the issue

I am using Next-auth with keycloak and docker-compose.

Everything works fine on my local computer, but when I use docker-compose i get this error:

arcade-iori         | [next-auth][error][GET_AUTHORIZATION_URL_ERROR]
arcade-iori         | https://next-auth.js.org/errors#get_authorization_url_error connect ECONNREFUSED 127.0.0.1:80 {
arcade-iori         |   message: 'connect ECONNREFUSED 127.0.0.1:80',
arcade-iori         |   stack: 'Error: connect ECONNREFUSED 127.0.0.1:80\n' +
arcade-iori         |     '    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1157:16)',
arcade-iori         |   name: 'Error'
arcade-iori         | }
arcade-iori         | [next-auth][error][SIGNIN_OAUTH_ERROR]
arcade-iori         | https://next-auth.js.org/errors#signin_oauth_error connect ECONNREFUSED 127.0.0.1:80 {
arcade-iori         |   error: {
arcade-iori         |     message: 'connect ECONNREFUSED 127.0.0.1:80',
arcade-iori         |     stack: 'Error: connect ECONNREFUSED 127.0.0.1:80\n' +
arcade-iori         |       '    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1157:16)',
arcade-iori         |     name: 'Error'
arcade-iori         |   },
arcade-iori         |   provider: {
arcade-iori         |     id: 'keycloak',
arcade-iori         |     name: 'Keycloak',
arcade-iori         |     wellKnown: 'http://abianic.test/auth/realms/myrealm/.well-known/openid-configuration',
arcade-iori         |     type: 'oauth',
arcade-iori         |     authorization: { params: [Object] },
arcade-iori         |     checks: [ 'pkce', 'state' ],
arcade-iori         |     idToken: true,
arcade-iori         |     profile: [Function: profile],
arcade-iori         |     clientId: 'myclientnext',
arcade-iori         |     clientSecret: 'Pw6ffETQgR5VLeXKL3v5jIsTjkNyvvCA',
arcade-iori         |     issuer: 'http://abianic.test/auth/realms/myrealm',
arcade-iori         |     signinUrl: 'http://abianic.test/api/auth/signin/keycloak',
arcade-iori         |     callbackUrl: 'http://abianic.test/api/auth/callback/keycloak'
arcade-iori         |   },
arcade-iori         |   message: 'connect ECONNREFUSED 127.0.0.1:80'
arcade-iori         | }

How to reproduce

This is the Next-auth config:

import NextAuth from "next-auth"
import KeycloakProvider from "next-auth/providers/keycloak";

export default NextAuth({
  debug: true,
  secret: process.env.SECRET,
  site: process.env.NEXTAUTH_URL,
  providers: [
    KeycloakProvider({
      clientId: 'myclientnext',
      clientSecret: 'Pw6ffETQgR5VLeXKL3v5jIsTjkNyvvCA...',
      issuer: 'http://abianic.test/auth/realms/myrealm',
      authorizationUrl: "http://abianic.test/auth/realms/myrealm/protocol/openid-connect/auth",
      accessTokenUrl: "http://abianic.test/auth/realms/myrealm/protocol/openid-connect/token",
      profileUrl: "http://abianic.test/auth/realms/myrealm/protocol/openid-connect/userinfo",
    })
  ],
})

This is my docker-compose.yml:

version: '3.7'

volumes:
  keycloak_db_data:
      driver: local

networks:
  arcadenet:
    driver: bridge

services:

  keycloak-db:
    image: postgres:11.2
    container_name: arcade-keycloak-db
    volumes:
      - keycloak_db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: arcadecloack
      POSTGRES_USER: arcade
      POSTGRES_PASSWORD: arcade
    networks:
      - arcadenet

  keycloak:
    image: quay.io/keycloak/keycloak:16.1.0
    container_name: arcade-keycloak
    environment:
      DB_VENDOR: POSTGRES
      DB_ADDR: arcade-keycloak-db
      DB_DATABASE: arcadecloack
      DB_USER: arcade
      DB_SCHEMA: public
      DB_PASSWORD: arcade
      KEYCLOAK_USER: admin4
      KEYCLOAK_PASSWORD: admin
      PROXY_ADDRESS_FORWARDING: true
      # Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
      #JDBC_PARAMS: "ssl=true"
    ports:
      - 8080:8080
    depends_on:
      - keycloak-db
    networks:
      - arcadenet

  iori:
    stdin_open: true # docker run -i
    tty: true        # docker run -t
    build:
        context: ../iori/
        dockerfile: Dockerfile
    image: iori
    container_name: arcade-iori
    restart: always
    ports:
      - 3000:3000
    volumes:
      - '../iori/:/app'
      - '/app/node_modules'
      - '/app/.next'
    environment:
      - CHOKIDAR_USEPOLLING=true
    networks:
      - arcadenet

  proxy:
    image: nginx
    container_name: arcade-proxy
    restart: unless-stopped
    ports:
      - 80:80
    volumes:
      - ./default-proxy.conf:/etc/nginx/conf.d/default.conf:ro
    networks:
      - arcadenet
    depends_on:
      - keycloak
      - iori

the env.local file:

NEXTAUTH_URL=http://abianic.test/
NEXTAUTH_URL_INTERNAL=http://arcade-iori:3000/
NEXTAUTH_SECRET=asd

This is the default-proxy.conf:

server {
    listen       80;
    listen  [::]:80;
    server_name  abianic.test;

    location /keycloak/ {
        proxy_pass          http://arcade-keycloak:8080/;
        proxy_set_header    Host               $host;
        proxy_set_header    X-Real-IP          $remote_addr;
        proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host   $host;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   $server_port;
        proxy_set_header    X-Forwarded-Proto  $scheme;
    }

    location /auth/ {
        proxy_pass          http://arcade-keycloak:8080/auth/;
        proxy_set_header    Host               $host;
        proxy_set_header    X-Real-IP          $remote_addr;
        proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Host   $host;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   $server_port;
        proxy_set_header    X-Forwarded-Proto  $scheme;
    }

    location / {

        proxy_pass http://arcade-iori:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;

    }

    # requests without trailing slash will be forwarded to include slash
    location = /backend {
        return 301 $scheme://$http_host$uri/$is_args$args;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

host file:

127.0.0.1 abianic.test

I think the problem is that if it's running in a Docker container then http://127.0.0.1:80 from the perspective of the Docker container is diferebt on each container, but I don't have "localhost/127.0.0.1" configured anywhere.

Expected behavior

verything works fine when I run the Next.js project on my local computer (http://localhost:3000/), but when I use docker-compose to run the project in a container I get the error

ndom91 commented 2 years ago

So NextAuth.js / the Next.js app is running inside a container as well? And it's tryign to connect to abianic.test from the looks of your error messages, right?

abianic.test seems to be mapped to 127.0.0.1 inside that container named arcade-iori. Meanign its trying to connect to port 80 inside its own container, where nothign is probably listening.

You'll have to set in the NextAuth.js config the keycloak URLs to something that the next.js container can reach. For example, the well-known URL should be something like this I think: http://arcade-keycloak:8080/auth/realms/myrealm/.well-known/openid-configuration

crisgarlez commented 2 years ago

So NextAuth.js / the Next.js app is running inside a container as well? And it's tryign to connect to abianic.test from the looks of your error messages, right?

abianic.test seems to be mapped to 127.0.0.1 inside that container named arcade-iori. Meanign its trying to connect to port 80 inside its own container, where nothign is probably listening.

You'll have to set in the NextAuth.js config the keycloak URLs to something that the next.js container can reach. For example, the well-known URL should be something like this I think: http://arcade-keycloak:8080/auth/realms/myrealm/.well-known/openid-configuration

thanks for reply @ndom91, after set the wellKnown parameter in the NextAuth.js config file.

export default NextAuth({
  debug: true,
  secret: process.env.NEXTAUTH_SECRET,
  site: process.env.NEXTAUTH_URL,
  providers: [
    KeycloakProvider({
      clientId: 'myclientnext',
      clientSecret: 'Pw6ffETQgR5VLeXKL3v5jIsTjkNyvvCA',
      issuer: 'http://abianic.test/auth/realms/myrealm',
      authorizationUrl: "http://abianic.test/auth/realms/myrealm/protocol/openid-connect/auth",
      accessTokenUrl: "http://abianic.test/auth/realms/myrealm/protocol/openid-connect/token",
      profileUrl: "http://abianic.test/auth/realms/myrealm/protocol/openid-connect/userinfo",
      wellKnown: "http://arcade-keycloak:8080/auth/realms/myrealm/.well-known/openid-configuration",
    })
  ],
})

the browser tries to redirect to the url:

http://arcade-keycloak:8080/auth/realms/myrealm/protocol/openid-connect/auth?client_id=myclientnext&scope=openid%20email%20profile&response_type=code&redirect_uri=http%3A%2F%2Fabianic.test%2Fapi%2Fauth% 2Fcallback%2Fkeycloak&state=RIctRwFCJ_QDggWogC21x95AqxWA1xOzo1UUQc1VUt0&codechallenge=N3I6UVWezrhw16mt-UtugZDbDf8zf1kA2N1eYeT0-8&code_challenge_method=S256

causing an error because internal Docker paths cannot be accessed from the browser

ndom91 commented 2 years ago

Ah okay yeah my bad, I misunderstood the setup a bit.

Where does abianic.test come from exactly? Shouldn't those all be http://project.test/auth/.. or whatever your proxy container is setup as?

crisgarlez commented 2 years ago

Ah okay yeah my bad, I misunderstood the setup a bit.

Where does abianic.test come from exactly? Shouldn't those all be http://project.test/auth/.. or whatever your proxy container is setup as?

http://project.test => http://abianic.test

My bad, I made some changes and forgot to put the updated messages with the correct host.

edemots commented 2 years ago

Hey! Do you have any news on this topic? I'm currently experimenting with the same issue with docker

crisgarlez commented 2 years ago

Hey! Do you have any news on this topic? I'm currently experimenting with the same issue with docker

my solution was to put a new entry in host file:

127.0.0.1      abianic.test
127.0.0.1      arcade-proxy

And point the [...nextauth].js file to the url with the keycloak container name:

import NextAuth from "next-auth"
import KeycloakProvider from "next-auth/providers/keycloak";

export default NextAuth({
  debug: true,
  secret: process.env.NEXTAUTH_SECRET,
  site: process.env.NEXTAUTH_URL,
  providers: [
    KeycloakProvider({
      clientId: process.env.KEYCLOAK_ID,
      clientSecret: process.env.KEYCLOAK_SECRET,
      issuer: process.env.KEYCLOAK_ISSUER,
      authorizationUrl: "http://arcade-proxy/auth/realms/myrealm/protocol/openid-connect/auth",
      accessTokenUrl: "http://arcade-proxy/auth/realms/myrealm/protocol/openid-connect/token",
      profileUrl: "http://arcade-proxy/auth/realms/myrealm/protocol/openid-connect/userinfo",
      wellKnown: "http://arcade-proxy/auth/realms/myrealm/.well-known/openid-configuration",
    })
  ],
})
edemots commented 2 years ago

my solution was to put a new entry in host file:

Thank you very much, kind of a workaround for the moment but it'll do the job!

samuel-esp commented 1 year ago

Actually having the same issue "connect ECONNREFUSED 127.0.0.1:8080" but my keycloak isn't deployed in a docker container. Has anyone found a working solution?

jaqxues commented 1 year ago

I think this should still be fixed at some point. Not being able to test OAuth with a local Docker deployment of every container is really annoying. Especially when you are used to running everything in containers and don't want to get everything installed.

I think it would be nice to allow (in the OAuth Interface itself) a parameter that works analogous to NEXTAUTH_URL_INTERNAL. So just some value that is used to parse the origin (scheme, domain, and most importantly port!), which is then used in every server side request.

If there are any downsides, please do let me know.

ndom91 commented 1 year ago

Yeah so the main issue here is that 127.0.0.1 is referring to different things from the perspective of each container / the host, right?

Some possible solutions / workarounds include:

I'll close this issue for now as we seem to have identified the root cause of the issue and others have provided some solutions. It's not really a next-auth code related issue, but rather a configuration error related to container networking.

ndom91 commented 1 year ago

Actually having the same issue "connect ECONNREFUSED 127.0.0.1:8080" but my keycloak isn't deployed in a docker container. Has anyone found a working solution?

Your Next.js application and Keycloak are deployed on the same server? Are you sure theres something listening on port 8080? You can check with sudo netstat -tlpn on most linux distros to show all open tcp ports in a list.

jaqxues commented 1 year ago

There is no way to change a port. So I would still like for there being a better way. The /etc/hosts file can only do domains. But the server is hosted in docker at 8080. and I have to map it to somewhere else.

ndom91 commented 1 year ago

Then your only option is to use a reverse proxy like nginx. That would, for example. take incoming requests on localhost:1234 and forward them on to localhost:8080, essentially making the service available under port 1234 as well.

Docs: https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/ Howto: https://gist.github.com/soheilhy/8b94347ff8336d971ad0

Are you sure theres no keycloak config to change the port it listens on though? That seems to be a pretty standard option.

Additionally, if you're running keycloak in a container you can use docker to remap the port it makes the service running in the container available on on the outside, for example with the docker flag -p 1234:8080 to remap 8080 internally in the container to 1234 on the outside.

wireless25 commented 1 year ago

Hey @crisgarlez, I'm at the exact same point now. Inside the container it does not work. Proxy is setup and everything is working fine, server-side the requests somehow still end up in "127.0.0.1" and not the proxyed url, therefore it fails because it's happening inside the container, as was described already above.

I did not quite understand your solution, what exactly is the "arcade-proxy" and where did you use the keycloak container name as domain name?

Thanks a lot for any support!

crisgarlez commented 1 year ago

Hey @crisgarlez, I'm at the exact same point now. Inside the container it does not work. Proxy is setup and everything is working fine, server-side the requests somehow still end up in "127.0.0.1" and not the proxyed url, therefore it fails because it's happening inside the container, as was described already above.

I did not quite understand your solution, what exactly is the "arcade-proxy" and where did you use the keycloak container name as domain name?

Thanks a lot for any support!

Hey @wireless25 "arcade-proxy" is the name for my Nginx Proxy container, if you look the "docker-compose.yml" file you find a service with the name "proxy" and "container_name: arcade-proxy". Inside of the NextJs container, we can point to "http://arcade-proxy/auth/" this means tha you are pointing to the Nginx Proxy, then the proxi redirects the request to "auth" to -> http://arcade-keycloak:8080/auth/ using the "location /auth/" configured in the default-proxy.conf.

But we have a problem, if you point to the "http://arcade-proxy/auth/" outside of the docker container, does not exists. Thats the reason beacause of we need to put the 127.0.0.1 arcade-proxy line in the host file, to access to the proxy with the url arcade-proxy inside or outside the docker container.

wireless25 commented 1 year ago

Hey @crisgarlez, I'm at the exact same point now. Inside the container it does not work. Proxy is setup and everything is working fine, server-side the requests somehow still end up in "127.0.0.1" and not the proxyed url, therefore it fails because it's happening inside the container, as was described already above. I did not quite understand your solution, what exactly is the "arcade-proxy" and where did you use the keycloak container name as domain name? Thanks a lot for any support!

Hey @wireless25 "arcade-proxy" is the name for my Nginx Proxy container, if you look the "docker-compose.yml" file you find a service with the name "proxy" and "container_name: arcade-proxy". Inside of the NextJs container, we can point to "http://arcade-proxy/auth/" this means tha you are pointing to the Nginx Proxy, then the proxi redirects the request to "auth" to -> http://arcade-keycloak:8080/auth/ using the "location /auth/" configured in the default-proxy.conf.

But we have a problem, if you point to the "http://arcade-proxy/auth/" outside of the docker container, does not exists. Thats the reason beacause of we need to put the 127.0.0.1 arcade-proxy line in the host file, to access to the proxy with the url arcade-proxy inside or outside the docker container.

@crisgarlez Thank you very much for taking the time and explaining the solution in detail. This helps a lot and I will try to implement it in my project soon.

StefanoOcchetti commented 10 months ago

I notice the same problem, but none of the workaround are applicable. The point is that the browser can access the Keycloak server at the same base address as my application (eg.: https://127.0.0.1 ), but the server should point to a named container, with even a different protocol (eg.: http://mykeycloak; notice the http, instead of https). The NEXTAUTH_URL_INTERNAL variable seems just to be ignored.

Koracs commented 6 months ago

Hey @crisgarlez, I'm at the exact same point now. Inside the container it does not work. Proxy is setup and everything is working fine, server-side the requests somehow still end up in "127.0.0.1" and not the proxyed url, therefore it fails because it's happening inside the container, as was described already above. I did not quite understand your solution, what exactly is the "arcade-proxy" and where did you use the keycloak container name as domain name? Thanks a lot for any support!

Hey @wireless25 "arcade-proxy" is the name for my Nginx Proxy container, if you look the "docker-compose.yml" file you find a service with the name "proxy" and "container_name: arcade-proxy". Inside of the NextJs container, we can point to "http://arcade-proxy/auth/" this means tha you are pointing to the Nginx Proxy, then the proxi redirects the request to "auth" to -> http://arcade-keycloak:8080/auth/ using the "location /auth/" configured in the default-proxy.conf.

But we have a problem, if you point to the "http://arcade-proxy/auth/" outside of the docker container, does not exists. Thats the reason beacause of we need to put the 127.0.0.1 arcade-proxy line in the host file, to access to the proxy with the url arcade-proxy inside or outside the docker container.

Hi @crisgarlez Thanks for writing about this issue. But i have one question: You put the host file entry at your local machine, right? Because we need to redirect the client side http call outside of the container. My understanding is that this solution works for your own machine, but how would we fix this error in production? I want to redirect users to the right adress but keep getting the unresolved host name (e.g. http://arcade-keycloak)