Skip to content

Commit 70dc902

Browse files
Add startup entrypoint to fix realm import bugs and enable portable hostnames
Keycloak's realm import silently drops the config for the 'username' protocol mapper in the 'profile' client scope, reverting it to defaults. The realm JSON also has redirect URIs and web origins hardcoded to a specific hostname, making the image non-portable across environments. This adds an entrypoint script that wraps kc.sh, waits for the realm import to complete, then idempotently patches the mapper config and (when SERVER_HOST is set) rewrites client URIs to match the deployment hostname. See: keycloak/keycloak#36065 See: keycloak/keycloak#16289 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6973cfa commit 70dc902

7 files changed

Lines changed: 860 additions & 26 deletions

File tree

.github/workflows/release.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@ jobs:
3030
run: |
3131
VERSION=${{ steps.get-version.outputs.version }}
3232
find . -name 'pom.xml' -exec mvn versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false -f {} \;
33-
- name: Build SPI plugin
34-
run: mvn --batch-mode -pl spi clean package
3533
- name: Build and push Docker image
36-
run: mvn --batch-mode package install
34+
run: mvn --batch-mode install
3735
- name: Release
3836
uses: softprops/action-gh-release@v1
3937
env:

.github/workflows/test.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- name: Build image
13+
run: docker build -t protegeproject/webprotege-keycloak:test .
14+
- name: Run entrypoint integration test
15+
run: ./test-entrypoint.sh

Dockerfile

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
1+
FROM alpine:3 AS tools
2+
ARG TARGETARCH
3+
RUN wget -O /usr/local/bin/jq \
4+
"https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-${TARGETARCH}" \
5+
&& chmod +x /usr/local/bin/jq
6+
7+
FROM maven:3.9-eclipse-temurin-17 AS spi-builder
8+
WORKDIR /build
9+
COPY spi/pom.xml .
10+
RUN mvn dependency:go-offline -B
11+
COPY spi/src ./src
12+
RUN mvn package -B -DskipTests
13+
114
FROM keycloak/keycloak:26.1
15+
COPY --from=tools /usr/local/bin/jq /usr/bin/jq
216
COPY ./webprotege /opt/keycloak/themes/webprotege
3-
COPY ./spi/target/webprotege-credential-check-authenticator-*.jar /opt/keycloak/providers/
17+
COPY --from=spi-builder /build/target/webprotege-credential-check-authenticator-*.jar /opt/keycloak/providers/
418
COPY ./webprotege.json /opt/keycloak/import/webprotege.json
19+
COPY --chmod=755 ./entrypoint.sh /opt/keycloak/bin/entrypoint.sh
20+
521
RUN /opt/keycloak/bin/kc.sh build
22+
23+
ENTRYPOINT ["/opt/keycloak/bin/entrypoint.sh"]

README.md

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,69 @@ Keycloak configuration for WebProtege. This repository contains:
88

99
## Prerequisites
1010

11-
- Java 17+
12-
- Maven 3.8+
1311
- Docker
1412

15-
## Building the Plugin
13+
## Docker Build
14+
15+
The `Dockerfile` uses a multi-stage build to compile the authenticator plugin,
16+
download tools, and package the theme, realm configuration, and startup
17+
entrypoint into a custom Keycloak image. No local Java or Maven installation
18+
is required.
1619

17-
The authenticator plugin must be built before deploying Keycloak:
20+
To build locally:
1821

1922
```bash
20-
cd spi
21-
mvn clean package
23+
docker build -t protegeproject/webprotege-keycloak:1.2.0 .
2224
```
2325

24-
This produces `spi/target/webprotege-credential-check-authenticator-1.0.0.jar`.
26+
## Startup Entrypoint
2527

26-
## Docker Build
28+
The image includes a custom entrypoint script (`entrypoint.sh`) that wraps the
29+
standard Keycloak startup. It starts Keycloak normally, waits for the realm
30+
import to complete, then applies two configuration patches that cannot be
31+
achieved through the realm import alone.
2732

28-
The `Dockerfile` packages the theme, plugin, and realm configuration into a custom Keycloak image:
33+
### 1. Protocol Mapper Fix
2934

30-
```dockerfile
31-
FROM keycloak/keycloak:26.1
32-
COPY ./webprotege /opt/keycloak/themes/webprotege
33-
COPY ./spi/target/webprotege-credential-check-authenticator-1.0.0.jar /opt/keycloak/providers/
34-
COPY ./webprotege.json /opt/keycloak/import/webprotege.json
35-
RUN /opt/keycloak/bin/kc.sh build
36-
```
35+
Keycloak's realm import mechanism has a known limitation: it silently drops the
36+
`config` dictionary for certain protocol mapper types. The realm JSON defines a
37+
`username` mapper in the `profile` client scope that maps the custom user
38+
attribute `webprotege_username` to the `preferred_username` JWT claim. After
39+
import, Keycloak reverts this mapper to its built-in default, which maps the
40+
Keycloak username field instead.
3741

38-
To build locally:
42+
This matters because WebProtege uses email addresses as Keycloak usernames, but
43+
internal application lookups rely on the original MongoDB user ID stored in the
44+
`webprotege_username` attribute. Without the fix, the backend receives email
45+
addresses where it expects user IDs, breaking user resolution.
3946

40-
```bash
41-
cd spi && mvn clean package && cd ..
42-
docker build -t protegeproject/webprotege-keycloak:1.0.0 .
43-
```
47+
The entrypoint detects this condition on each startup and, if the mapper is in
48+
the wrong state, deletes it and recreates it with the correct configuration.
49+
On subsequent boots where the mapper is already correct, the fix is skipped.
50+
51+
### 2. Hostname-Based Client URI Patching
52+
53+
The realm JSON ships with a default hostname baked into the `webprotege`
54+
client's redirect URIs, web origins, and base URL. Self-hosted deployments use
55+
different hostnames. When the `SERVER_HOST` environment variable is set, the
56+
entrypoint updates these values so that:
57+
58+
- Keycloak accepts OAuth redirects back to the correct host
59+
- CORS headers include the correct origin
60+
- The Keycloak login and account pages link back to the correct application URL
61+
- The realm's OpenID Connect discovery document advertises the correct issuer
62+
63+
This makes the image portable — the same build works for local development,
64+
staging, and production by setting `SERVER_HOST` in the deployment environment.
65+
66+
### Environment Variables
67+
68+
| Variable | Required | Default | Purpose |
69+
|---|---|---|---|
70+
| `KEYCLOAK_ADMIN` | Yes | `admin` | Admin username for kcadm authentication |
71+
| `KEYCLOAK_ADMIN_PASSWORD` | Yes | `password` | Admin password for kcadm authentication |
72+
| `KC_HTTP_RELATIVE_PATH` | Yes | *(none)* | Keycloak's HTTP relative path (e.g. `/keycloak`) |
73+
| `SERVER_HOST` | No | *(none)* | Public hostname; when set, client URIs and the realm frontend URL are updated to match |
4474

4575
## Deployment
4676

0 commit comments

Comments
 (0)