Skip to content

feat(compose): provision Keycloak with the realm the http profile needs - #192

Open
adityamparikh wants to merge 1 commit into
apache:mainfrom
adityamparikh:feat/keycloak-compose
Open

feat(compose): provision Keycloak with the realm the http profile needs#192
adityamparikh wants to merge 1 commit into
apache:mainfrom
adityamparikh:feat/keycloak-compose

Conversation

@adityamparikh

Copy link
Copy Markdown
Contributor

Problem

The http profile authenticates against Keycloak, but nothing started one. A developer had to run
the container by hand and then script a realm, a client and an audience mapper before the server
would boot.

The mapper is the part that is easy to miss, and it fails quietly:

mapper missing  →  token issued normally  →  server answers 401

Keycloak does not honour the RFC 8707 resource= parameter, so validateAudienceClaim(true) finds
no matching aud. Nothing in the token request hints at a problem.

Change

compose.yaml gains a keycloak service that imports keycloak/solr-mcp-realm.json at startup, so
the realm, both clients and the mapper exist before the server asks for a token. The import covers
what the Quick Start created by hand:

Client / user Purpose
solr-mcp-service Confidential, service accounts enabled — machine-to-machine callers
solr-mcp-client Public, redirect URIs for MCP Inspector
testuser / testpassword The password-grant examples in keycloak.md

Both clients carry the audience mapper for http://localhost:8080/mcp. The credentials are
development credentials, committed on purpose; a real deployment provisions its own.

The healthcheck is load-bearing

keycloak.md documents an ordering constraint — "Verify the realm resolves BEFORE starting the
server… The server fails to boot if this is not a 200."
The server resolves the issuer while
building its JWT decoder, so it cannot start before Keycloak is answering.

Declaring a healthcheck lets Spring Boot's compose support wait for the container instead of leaving
that to the developer. Keycloak's image ships neither curl nor wget — I checked — so the probe
goes through bash's /dev/tcp against the management port.

Verification

Run from this compose file (on a spare port, to avoid colliding with an already-running instance):

Check Result
Container health healthy via the declared healthcheck
realms/solr-mcp/.well-known/openid-configuration 200
client_credentials on solr-mcp-service aud: ["http://localhost:8080/mcp", "account"]
Password grant on solr-mcp-client as testuser aud: "http://localhost:8080/mcp"

Not covered: an end-to-end call against a running MCP server. The test instance was on a spare port,
so its iss would not match a server configured for :8180. The audience claim — what this change
exists to guarantee — is verified above.

🤖 Generated with Claude Code

https://claude.ai/code/session_011nUD34DFfoJeyQRTquPy7a

The http profile authenticates against Keycloak, but nothing started one. A
developer had to run the container by hand and then script a realm, a client
and an audience mapper before the server would boot — and the mapper is easy to
miss, because skipping it produces a token that is issued normally and then
refused with 401.

compose.yaml now defines a keycloak service that imports
keycloak/solr-mcp-realm.json, so the realm, both clients and the mapper exist
before the server asks for a token. The import covers what the Quick Start
created by hand: solr-mcp-service (confidential, service accounts) for
machine-to-machine callers, solr-mcp-client (public) for MCP Inspector, and
testuser. The credentials in it are development credentials, committed on
purpose; a real deployment provisions its own.

The healthcheck is load-bearing rather than decoration. The server resolves the
issuer while building its JWT decoder and fails to boot if the realm is not yet
answering, which is the ordering constraint keycloak.md warns about; declaring a
healthcheck makes Spring Boot's compose support wait for the container instead.
Keycloak's image ships neither curl nor wget, so the probe goes through bash's
/dev/tcp against the management port.

Verified by running the service from this compose file: the container reaches
healthy, the realm resolves, and both grants return tokens carrying
aud http://localhost:8080/mcp — client_credentials for the service client and
the password grant for the imported test user.

Signed-off-by: Aditya Parikh <aditya.m.parikh@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011nUD34DFfoJeyQRTquPy7a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant