I have a java web application with tomcat and keycloak for authentication. Both services (the app and Keycloak) are running in separate Docker containers, connected through a docker bridge network
keycloak
service runs at http://keycloak:8080/auth
inside Docker.In my app profile was configured with keycloak.json as:
{
"realm": "${realm}",
"auth-server-url": "http://keycloak:8080/auth",
"ssl-required": "none",
"resource": "${client_id}",
"public-client": true,
"confidential-port": 0
}
I had a problem when trying to access http://localhost:8090/system
, the app redirected the browser to this Keycloak URL:
http://keycloak:8080/auth/realms/tdoc-api/protocol/openid-connect/auth...
Which failed on browser showingERR_CONNECTION_REFUSED
, cause I understand this service can not be resolved externally from the host.
Also I tried changing keycloak.json to:
{
"realm": "${realm}",
"auth-server-url": "http://localhost:8012/auth",
"ssl-required": "none",
"resource": "${client_id}",
"public-client": true,
"confidential-port": 0
}
But my app failed when trying to log in showing a 500 internal server error:
[WARN ] 12:08:46.150 org.keycloak.adapters.KeycloakDeployment.resolveUrls() - Failed to load URLs from http://localhost:8012/auth/realms/${realm}/.well-known/openid-configuration
java.net.ConnectException: Connection refused (Connection refused)
Because it can not resolve from inside the container my service exposed locally in 8012 port. Which I think was the answer provided in this post: https://stackoverflow.com/a/74929659
Does anyone know why didnt work for me this solution?
I was able to fix it setting a environment in docker-compose.yml:
More context of my docker environment:
version: '3'
services:
app-db:
image: mysql:5.7
ports:
- "3309:3306"
environment:
MYSQL_ROOT_PASSWORD: example
networks:
- my-network
my-app:
build: .
ports:
- "8090:8080"
- "8000:8000"
environment:
- JAVA_OPTS=...
volumes:
- ./app:/usr/local/tomcat/webapps/my-app
depends_on:
- app-db
networks:
- my-network
keycloak:
image: quay.io/keycloak/keycloak:16.1.1
ports:
- "8012:8080"
environment:
- DB_VENDOR=mysql
- DB_ADDR=keycloak-db
- DB_PORT=3306
- DB_USER=keycloak_user
- DB_PASSWORD=keycloak_pass
- DB_DATABASE=keycloak
- PROXY_ADDRESS_FORWARDING=true
- KEYCLOAK_FRONTEND_URL=http://localhost:8012/auth
volumes:
- ./themes:/opt/jboss/keycloak/themes
depends_on:
- keycloak-db
networks:
- my-network
keycloak-db:
image: mysql:5.7
ports:
- "3307:3306"
environment:
- MYSQL_ROOT_PASSWORD=keycloak_pass
- MYSQL_DATABASE=keycloak
- MYSQL_USER=keycloak_user
- MYSQL_PASSWORD=keycloak_pass
volumes:
- ./volumes/mysql:/var/lib/mysql
networks:
- my-network
networks:
my-network:
external: true
name: my-network
Why http://keycloak:8080/auth
didn't work?
As you noticed, this works only inside Docker, where keyclock
is resolvable via the internal Docker network. But when the browser gets redirected to that URL, it tries to resolve http://keycloak:8080
from your host OS, which fails — because keycloak
is not a real hostname on your host machine. That's why it ends up with ERR_CONNECTION_REFUSED
error.
Why http://localhost:8012/auth
didn't work?
With this auth-server-url
, your app (inside the my-app
container) tried to connect to localhost:8012
, but in container-land, localhost
means "inside this container", not your host machine.
So it's trying to find Keycloak inside the same container (where it doesn't exist), and fails with Connection refused
exception.
Why KEYCLOAK_FRONTEND_URL=http://localhost:8012/auth
works as expected?
When you set KEYCLOAK_FRONTEND_URL
, you told Keycloak itself: "When generating URLs (like the redirect URI), use http://localhost:8012/auth
."
This works because:
Your Java app talks to Keycloak using the internal http://keycloak:8080/auth
URL.
The browser gets redirected to the correct host-facing URL: http://localhost:8012/auth
.
That solves both sides:
internal container -> container hostname
browser -> localhost-bound service