Issues running keycloak in docker and authenticating with authjs

I’m running keycloak in docker with nextjs and I’m using authjs to integrate with keycloak but I’m running into issues trying to get it working.

It works fine if I run keycloak in docker and my nextjs app locally but once I put everything in docker-compose it just refuses to connect.

I made a repo with an example you can test by just running docker-compose up to replicate the issue I also documented all my findings with things I’ve tried in order to resolve this issue.

Any help trying to solve this is greatly appreciated, this is the link to my example project: GitHub - benmarte/authjs-docker: Nextjs, Authjs and keycloak running in docker

Thanks.

Hey, what errors are you getting when attempting to authenticate?

This is all I got to go by:

2024-06-11 19:01:31 CREATE_STATE [
2024-06-11 19:01:31   {
2024-06-11 19:01:31     value: 'eyJyYW5kb20iOiJleWRJYS1CaFVocmtsMjQ2MkpKemViUmpnV0lTT2x2WmxDdjVJTHlvRzVzIn0',
2024-06-11 19:01:31     maxAge: 900
2024-06-11 19:01:31   }
2024-06-11 19:01:31 ]
2024-06-11 19:01:31 CREATE_PKCECODEVERIFIER [
2024-06-11 19:01:31   { value: 'zkAFnughK65TNAzcahaEub8KjCn5ln2eedYoepdxJLE', maxAge: 900 }
2024-06-11 19:01:31 ]
2024-06-11 19:01:31 authorization url is ready [
2024-06-11 19:01:31   {
2024-06-11 19:01:31     url: URL {
2024-06-11 19:01:31       href: 'http://keycloak.local/realms/webapp/protocol/openid-connect/auth?scope=openid+profile+email&response_type=code&client_id=webapp&redirect_uri=http%3A%2F%2Fwebapp.local%2Fauth%2Fcallback%2Fkeycloak&state=eyJyYW5kb20iOiJleWRJYS1CaFVocmtsMjQ2MkpKemViUmpnV0lTT2x2WmxDdjVJTHlvRzVzIn0&code_challenge=k9EVdVHZS9lni0U0iu3vdeTO5su51bXhNAcmMrRWjmE&code_challenge_method=S256',
2024-06-11 19:01:31       origin: 'http://keycloak.local',
2024-06-11 19:01:31       protocol: 'http:',
2024-06-11 19:01:31       username: '',
2024-06-11 19:01:31       password: '',
2024-06-11 19:01:31       host: 'keycloak.local',
2024-06-11 19:01:31       hostname: 'keycloak.local',
2024-06-11 19:01:31       port: '',
2024-06-11 19:01:31       pathname: '/realms/webapp/protocol/openid-connect/auth',
2024-06-11 19:01:31       search: '?scope=openid+profile+email&response_type=code&client_id=webapp&redirect_uri=http%3A%2F%2Fwebapp.local%2Fauth%2Fcallback%2Fkeycloak&state=eyJyYW5kb20iOiJleWRJYS1CaFVocmtsMjQ2MkpKemViUmpnV0lTT2x2WmxDdjVJTHlvRzVzIn0&code_challenge=k9EVdVHZS9lni0U0iu3vdeTO5su51bXhNAcmMrRWjmE&code_challenge_method=S256',
2024-06-11 19:01:31       searchParams: URLSearchParams {
2024-06-11 19:01:31         'scope' => 'openid profile email',
2024-06-11 19:01:31         'response_type' => 'code',
2024-06-11 19:01:31         'client_id' => 'webapp',
2024-06-11 19:01:31         'redirect_uri' => 'http://webapp.local/auth/callback/keycloak',
2024-06-11 19:01:31         'state' => 'eyJyYW5kb20iOiJleWRJYS1CaFVocmtsMjQ2MkpKemViUmpnV0lTT2x2WmxDdjVJTHlvRzVzIn0',
2024-06-11 19:01:31         'code_challenge' => 'k9EVdVHZS9lni0U0iu3vdeTO5su51bXhNAcmMrRWjmE',
2024-06-11 19:01:31         'code_challenge_method' => 'S256' },
2024-06-11 19:01:31       hash: ''
2024-06-11 19:01:31     },
2024-06-11 19:01:31     cookies: [ [Object], [Object] ],
2024-06-11 19:01:31     provider: {
2024-06-11 19:01:31       id: 'keycloak',
2024-06-11 19:01:31       name: 'Keycloak',
2024-06-11 19:01:31       type: 'oidc',
2024-06-11 19:01:31       style: [Object],
2024-06-11 19:01:31       clientId: 'webapp',
2024-06-11 19:01:31       clientSecret: 'MohY0/2zSQw/psWEnejC2ka3Al0oifvY4YjOkUaFfnI=',
2024-06-11 19:01:31       issuer: 'http://keycloak.local/realms/webapp',
2024-06-11 19:01:31       authorization: [Object],
2024-06-11 19:01:31       wellKnown: 'http://keycloak.local/realms/webapp/.well-known/openid-configuration',
2024-06-11 19:01:31       token: [Object],
2024-06-11 19:01:31       userinfo: [Object],
2024-06-11 19:01:31       signinUrl: 'http://webapp.local/auth/signin/keycloak',
2024-06-11 19:01:31       callbackUrl: 'http://webapp.local/auth/callback/keycloak',
2024-06-11 19:01:31       redirectProxyUrl: 'http://webapp.local/auth//callback/keycloak',
2024-06-11 19:01:31       checks: [Array],
2024-06-11 19:01:31       profile: [Function: C],
2024-06-11 19:01:31       account: [Function: I]
2024-06-11 19:01:31     }
2024-06-11 19:01:31   }
2024-06-11 19:01:31 ]
2024-06-11 19:01:44 callback route error details [
2024-06-11 19:01:44   {
2024-06-11 19:01:44     method: 'GET',
2024-06-11 19:01:44     query: {
2024-06-11 19:01:44       state: 'eyJyYW5kb20iOiJleWRJYS1CaFVocmtsMjQ2MkpKemViUmpnV0lTT2x2WmxDdjVJTHlvRzVzIn0',
2024-06-11 19:01:44       session_state: 'd0517a2f-75e6-4487-b787-c6ce126a961e',
2024-06-11 19:01:44       iss: 'http://keycloak.local/realms/webapp',
2024-06-11 19:01:44       code: '7f75a7d9-9d37-4a9c-b9d1-75f1e4391abf.d0517a2f-75e6-4487-b787-c6ce126a961e.61010cc3-48ae-401b-9764-e086e4b43814'
2024-06-11 19:01:44     },
2024-06-11 19:01:44     body: undefined
2024-06-11 19:01:44   }
2024-06-11 19:01:44 ]
2024-06-11 19:01:39 env-url-basepath-redundant []
2024-06-11 19:01:44 a: Read more at https://errors.authjs.dev#callbackrouteerror
2024-06-11 19:01:44     at is (/app/.next/server/chunks/428.js:393:38923)
2024-06-11 19:01:44     at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
2024-06-11 19:01:44     at async im (/app/.next/server/chunks/428.js:393:45531)
2024-06-11 19:01:44     at async ig (/app/.next/server/chunks/428.js:393:50443)
2024-06-11 19:01:44     at async /app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:42484
2024-06-11 19:01:44     at async eI.execute (/app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:32486)
2024-06-11 19:01:44     at async eI.handle (/app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:43737)
2024-06-11 19:01:44     at async doRender (/app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/server/base-server.js:1317:42)
2024-06-11 19:01:44     at async cacheEntry.responseCache.get.routeKind (/app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/server/base-server.js:1539:28)
2024-06-11 19:01:44     at async NextNodeServer.renderToResponseWithComponentsImpl (/app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/server/base-server.js:1447:28) {
2024-06-11 19:01:44   type: 'CallbackRouteError',
2024-06-11 19:01:44   kind: 'error',
2024-06-11 19:01:44   [cause]: {
2024-06-11 19:01:44     err: TypeError: fetch failed
2024-06-11 19:01:44         at node:internal/deps/undici/undici:12502:13
2024-06-11 19:01:44         at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
2024-06-11 19:01:44         at async globalThis.fetch (/app/.next/server/chunks/675.js:1:36483)
2024-06-11 19:01:44         at async t2 (/app/.next/server/chunks/428.js:393:27136)
2024-06-11 19:01:44         at async is (/app/.next/server/chunks/428.js:393:34159)
2024-06-11 19:01:44         at async im (/app/.next/server/chunks/428.js:393:45531)
2024-06-11 19:01:44         at async ig (/app/.next/server/chunks/428.js:393:50443)
2024-06-11 19:01:44         at async /app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:42484
2024-06-11 19:01:44         at async eI.execute (/app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:32486)
2024-06-11 19:01:44         at async eI.handle (/app/node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0__react@18.2.0/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js:6:43737) {
2024-06-11 19:01:44       [cause]: [Error]
2024-06-11 19:01:44     },
2024-06-11 19:01:44     errno: -3001,
2024-06-11 19:01:44     code: 'EAI_AGAIN',
2024-06-11 19:01:44     syscall: 'getaddrinfo',
2024-06-11 19:01:44     hostname: 'keycloak.local',
2024-06-11 19:01:44     provider: 'keycloak'
2024-06-11 19:01:44   }

What weird is when you try to login the session gets created in keycloak so I’m trying to figure out if its a keycloak issue or a authjs issue.

I’ve gotten in contact with the authjs team but they keep saying its a configuration issue: AuthJS do not redirect to set URL, instead it redirect to Docker container internal IP · nextauthjs/next-auth · Discussion #8449 · GitHub

Thanks.

From what I am seeing, you are having a DNS resolution issue (EAI_AGAIN) when attempting to get the ip address of keycloak.local and from github, your service name is keycloak. I don’t know much about traefik, but it looks like you want keycloak.local to redirect to keycloak?

Try using either keycloak or your docker bridge ip (without going through traefik). See if it changes anything. i.e. http://keycloak.local/realms/webapp ==> http://keycloak/realms/webapp

If I do that I get an error in my nextjs app since the browser can’t access the docker bridge address when authenticating and I get an invalid redirect uri error.

I see. It is definitely configuration then. You have to find a way to use localhost when outside the compose services network. For the latter, you must use keycloak or the bridge between your containers. I don’t have experience with your stack to tell you the exact way, but you can look around

Thanks for your assistance, I appreciate it :+1:

1 Like

Hello @benmarte,
It looks like you are having a network issue. I explain how you can setup this environment in this post:

I used next-auth but still if you take a look at the .env.local and route.ts configuration I’m pretty sure you can solve it.

Take care and let me know if you have any questions!

@ozdemirrulass Unfortunately it does not work with the latest version of Authjs :frowning:

I’ve tried using a proxy and that does not work either I’m almost positive it’s a docker-compose issue now after doing some research.

If I figure it out I will make sure to post back, thanks again for posting this.

Very nice tutorial btw.

1 Like

Interesting. Will try to reproduce using Auth.js as soon as possible. I’ll keep you posted if I have any luck.
Thanks for the compliments on tutorial.

Awesome, I look forward to seeing if you run into the same issues I’m also using the latest version of keycloak which is 24.

I did notice you disabled the direct access grants checkbox I was not doing that so I’m going to try that and see if it does anything.

I’ve only gotten nextjs to work as a public client.

@benmarte I just wrote a new article to explain how to solve your problem step by step and it also contains the end source code as a github repository. It’s %100 solution and tested. Let me know if you stuck.

Here is the link: Enterprise-Level Authentication in a Containerized Environment NextJS

I’d be happy to connect on LinkedIn btw! It’s always nice to keep in touch with people who works on similar technologies. Ulaş Özdemir - Jara | LinkedIn

More precise explanation on your problem:

Docker compose allows your containers to communicate through docker networks and your NextJS application requires endpoints to handle authentication flow but beside the endpoints it also requires authorization url parameter and issuer url parameter to match it with your realm credentials.

While using docker any kind of connectivity related url must use the container name but parameters must be localhost.

Example Keycloak Provider:

Keycloak({
      jwks_endpoint: `${process.env.NEXT_CONTAINER_KEYCLOAK_ENDPOINT}/realms/myrealm/protocol/openid-connect/certs`,
      wellKnown: undefined,
      clientId: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID,
      clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
      issuer: `${process.env.NEXT_LOCAL_KEYCLOAK_URL}/realms/${process.env.NEXT_PUBLIC_KEYCLOAK_REALM}`,
      authorization: {
        params: {
          scope: "openid email profile",
        },
        url: `${process.env.NEXT_LOCAL_KEYCLOAK_URL}/realms/myrealm/protocol/openid-connect/auth`,
      },
      token: `${process.env.NEXT_CONTAINER_KEYCLOAK_ENDPOINT}/realms/myrealm/protocol/openid-connect/token`,
      userinfo: `${process.env.NEXT_CONTAINER_KEYCLOAK_ENDPOINT}/realms/myrealm/protocol/openid-connect/userinfo`,
    }),

Hope it makes sense.

1 Like

Dude thank you so much it finally works but it was more involved than just the provider settings I had to make networks for the containers which is weird since I thought if they were all part of the same network they would be able to talk to each other but apparently, that was not the case.

I appreciate your help, my friend have a great day.

You’re welcome @benmarte!

In my first article I explain the network and connectivity in dept for our case.

When using Docker Compose to define multiple services, Docker Compose creates a bridge network by default and assigns a unique name to each container within that network. By specifying a container name in Docker Compose, you are giving that container a human-readable alias within the context of the Docker network.

If you want to isolate the connectivity between your containers you may specify custom networks for this purpose. It is also covered in the here in ARTICLE

Just leaving these for people who may have the same problem in the future.

I’m glad we managed the solve the issue! Have a great day mate and I’m happy that you’ve sent a request on LinkedIn. Can’t wait to see your future works!

1 Like

Just a minor clarification for those reading the thread.
If your application is an SPA (browser-based app), you MUST use an OAuth Public Client. If your application has a backend, then you can use an OAuth Confidential Client.
When you initialize a library, I recommend using the OIDC well-known endpoint for the discovery of the endpoint (if supported).
Lastly, for clarity, if you visit the following link [1] from authjs.dev, it mentions OAuth as authentication. That is incorrect. OAuth is an authorization standard, and OpenID Connect (OIDC) is for authentication.

[1] OAuth Providers

3 Likes

Completely agree!
@dasniko elegantly explains the subject in this video: https://www.youtube.com/watch?v=2cNNdnM2sT0

2 Likes