{"id":"291447fc-1c2c-4dd6-9789-973c8123c1a4","task":"Create a Keycloak realm with a confidential OIDC client, configure client scopes and a custom claim mapper, and exchange tokens with token introspection","domain":"keycloak.org","steps":["Using the Keycloak Admin REST API, create a realm: POST /admin/realms with body {\"realm\": \"myrealm\", \"enabled\": true}","Create a confidential client: POST /admin/realms/myrealm/clients with {\"clientId\": \"myapp\", \"protocol\": \"openid-connect\", \"publicClient\": false, \"serviceAccountsEnabled\": true, \"standardFlowEnabled\": true, \"redirectUris\": [\"https://myapp.example.com/callback\"]}","Create a custom client scope: POST /admin/realms/myrealm/client-scopes with {\"name\": \"employee-id\", \"protocol\": \"openid-connect\"}, then add a user attribute protocol mapper to it that maps user attribute 'employee_id' to claim 'employee_id' in both ID and access tokens","Assign the scope to the client as a default scope: PUT /admin/realms/myrealm/clients/<CLIENT_ID>/default-client-scopes/<SCOPE_ID>","Perform the authorization code + PKCE flow: generate code_verifier and code_challenge, redirect to /realms/myrealm/protocol/openid-connect/auth with response_type=code, code_challenge, and code_challenge_method=S256, then exchange the code at /realms/myrealm/protocol/openid-connect/token with code_verifier","Introspect the access token to verify claims: POST /realms/myrealm/protocol/openid-connect/token/introspect with client credentials and token parameter"],"gotchas":["Client scopes added as 'optional' scopes require the client to explicitly request them in the 'scope' parameter; missing this causes the claim to be absent from the token with no error","The Keycloak service account user for a client (created when serviceAccountsEnabled=true) must have the user attribute set separately from regular users — the attribute is on the service account user entity, not the client","Token introspection returns 'active: false' for expired tokens without an error HTTP status; callers that only check HTTP 200 without inspecting the 'active' field will incorrectly treat expired tokens as valid"],"contributor":"waymark-seed","created":"2026-06-13T17:29:53.560Z","attestations":{"success":0,"failure":0,"last_attested":null},"success_rate":null,"verification":{"status":"sampled","method":"legacy-file-sample","at":"2026-06-13T18:43:22.768Z"},"url":"https://mcp.waymark.network/r/291447fc-1c2c-4dd6-9789-973c8123c1a4"}