Skip to content

fix(keycloak): add omitempty to protocolMapperRep.ID to prevent ghost PK - #362

Merged
cwiklik merged 1 commit into
rossoctl:mainfrom
cwiklik:fix/audience-mapper-empty-id
May 14, 2026
Merged

fix(keycloak): add omitempty to protocolMapperRep.ID to prevent ghost PK#362
cwiklik merged 1 commit into
rossoctl:mainfrom
cwiklik:fix/audience-mapper-empty-id

Conversation

@cwiklik

@cwiklik cwiklik commented May 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add omitempty to protocolMapperRep.ID JSON tag so the zero-value "" is omitted from POST payloads, allowing Keycloak to auto-generate mapper UUIDs

Root cause

The protocolMapperRep struct serializes the zero-value ID field as "id":"" in POST requests. Keycloak accepts this as an explicit primary key, inserting a row with PK "" into the PROTOCOL_MAPPER table. Every subsequent mapper creation — for any agent, any scope — fails with:

duplicate key value violates unique constraint "constraint_pcm"
Detail: Key (id)=() already exists.

The operator's 409 handler does a GET to verify the mapper exists, but the ghost row (id="") is not returned by the Keycloak REST API. The handler returns nil, the next reconcile retries with the same result, and the mapper is never created. This causes "aud" not satisfied 401 errors on all agents with AuthBridge enabled.

Affected versions

This affects all deployments where the operator creates audience scopes (PR #360 / EnsureAudienceScope). The first agent deployed poisons the DB for all subsequent agents.

Recovery

Existing deployments with the ghost row need a one-time DB cleanup:

DELETE FROM protocol_mapper_config WHERE protocol_mapper_id = '';
DELETE FROM protocol_mapper WHERE id = '';

After the cleanup + operator upgrade, new agents get correct audience mappers automatically.

Test plan

  • Verified ghost row in Keycloak Postgres (SELECT * FROM protocol_mapper WHERE id = '')
  • Deleted ghost row, rebuilt operator with fix, deployed to Kind
  • New agent with AuthBridge: audience mapper created with proper UUID, no 401

Assisted-By: Claude Code

The protocolMapperRep struct serializes the zero-value ID field as
"id":"" in POST requests. Keycloak accepts this as an explicit primary
key, creating a row with PK "". Every subsequent mapper creation for
any scope fails with duplicate key constraint violation on
PROTOCOL_MAPPER.id. The 409 handler cannot find the ghost row via GET,
so it returns nil and the mapper is never created. This causes "aud not
satisfied" 401 errors on all agents with AuthBridge.

Adding omitempty causes the ID field to be omitted from POST payloads
so Keycloak auto-generates a UUID.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: cwiklik <cwiklikj@gmail.com>
@cwiklik
cwiklik merged commit c88be40 into rossoctl:main May 14, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants