Build1 distinct publisher3 min readPublished
The S256 pin is a per-client attribute, and the executor that builds a client from a published metadata document sets three attributes, all unrelated to PKCE. The executor addition imports cleanly, yet it never runs.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Client policies work by voting. On each event every condition in the policy is asked whether it applies, and the policy needs at least one yes before the executors in its profile run [17]. `ClientIdUriSchemeCondition` has exactly one case in its switch statement, `PRE_AUTHORIZATION_REQUEST`, and every other event falls through to `default` and returns `ABSTAIN` [14]. This is a deliberate design choice, not an oversight: pre-authorization is the only stage where a `client_id` that happens to be a URL means anything, because no client has been resolved yet [15].
`PKCEEnforcerExecutor` is built for the other half of the lifecycle. It acts on four events: `REGISTER` and `UPDATE`, where it auto-configures and validates the pin on a proposed client, and `AUTHORIZATION_REQUEST` and `TOKEN_REQUEST`, where it checks the live request [16]. One voting event plus four acting events gives five distinct events with none shared [21]. On the event the condition votes on, the enforcer has no handler; on the events the enforcer handles, the only condition in the policy abstains, and for a single-condition policy an abstention is the entire vote [20]. Add a second condition that votes on `AUTHORIZATION_REQUEST` and the policy can be satisfied by that one instead [18].
The missing pin reads better as inheritance than as a PKCE hole. The same provisioned client came up with no `basic` scope, and therefore no `sub` claim, no audience mapper, and none of the realm's capability scopes [7]. A provisioned client takes the realm's default client scopes, and in a realm where every client had always named its own, none had ever been declared [8]. The executor reads from a level this realm never populated.
The transfer feature follows the same pattern. It exists for a draft standard where a client identifies itself by URL instead of pre-registering, and it gets switched on because that is how an MCP client registers with an authorization server it has never met [9]. You are exposed if two things hold: the feature is on, and your S256 floor lives in each client's own JSON [5] rather than in a policy whose conditions vote on the live authorization and token events [12]. Declare default client scopes and pin PKCE through a policy that fires on live requests, and the same inheritance runs in your favour.
Keycloak already says the shape of this out loud. The Server Administration Guide's Conditions evaluation section notes that some condition types are evaluated in all request types while others only during certain requests, that "in some cases, it may not be obvious whether particular condition is evaluated or not", and that you should test your scenarios [10]. That text lives in the guide covering client policies, but the guide that walks admins through enabling the feature omits it at 26.7.1 or 26.7.3, and so does the page as published on 2 September 2026 [11]. Meanwhile the admin console shows the enforcer sitting inside the profile, which is accurate and no help whatsoever [19].
What catches it is a live authorization request with `plain`, or with no `code_challenge` at all, against a client the executor built [3].
Ranked by verification strength, evidence, and original report placement.
The testing was done on Keycloak 26.7.1, in a realm where every hand-authored client carries "pkce.code.challenge.method": "S256" and refuses plain.
With the OAuth Client ID Metadata Document feature enabled, an authorization request that uses a published metadata document as its client_id causes Keycloak to fetch that document and provision a client from it.
The client Keycloak provisioned from the metadata document accepted plain, and also accepted a request carrying no code_challenge at all.
Nothing was misconfigured: the realm's PKCE settings were what they had always been, the feature was enabled the way its guide describes, and the client policy gating the feature was doing exactly its job.
The PKCE pin is a client attribute; every authored client in the realm names it in its own JSON, and that attribute is what makes those clients refuse plain.
The CIMD provider sets three attributes on the client it creates: consent required, display on the consent screen, and the consent text naming the document's host. No PKCE key is among them, and the CIMD provider source contains no reference to PKCE anywhere.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 2, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Cognito rejects RFC 8707, so it cannot be the authorization server for an MCP client1 distinct publisher
build
Atlas App Connections hands AI clients your whole Atlas role, so fix the roles first1 distinct publisher
build
Once an agent can write, the wrong post looks exactly like the right one1 distinct publisher
build
Pandex hooked a Fortune 500 agent four minutes after claiming a package name from llms.txt1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Specific, single-handed, unchecked
The technical work is unusually falsifiable — a version, a realm, a named condition with one switch case, a named executor with four events — and that specificity is most of what earns trust here. But every one of those facts arrives from the same developer's afternoon, with Keycloak present only as quoted documentation. Nobody has re-run the plain request in a second realm, and the claim that the setup guide omits the warning at both releases is an assertion of absence resting on one person's reading.
One realm, one version
What we can actually see deployed is a single realm on 26.7.1 belonging to the person writing about it. The feature's real pull is credible — MCP clients need some way to register with a server they have never met, and Keycloak's own MCP guide points at this path — but credible demand is not observed usage, and no other deployment, operator or vendor appears anywhere in this reporting.
Underplayed for what it describes
An author with an appetite for alarm had one available: an authorization server accepting plain PKCE, and a no-challenge request, from a client it built itself. Instead the condition's single switch case is called defensible, the abstention finding is narrowed to single-condition policies before anyone can over-read it, and the headline promises inertness rather than exposure. The restraint is the right instinct and it still leaves the security consequence quieter than the facts warrant — sharpened only slightly by the absence of any independent confirmation to escalate from.
No product in the frame
Nothing in this account is selling anything: no tool, no consultancy, no competing identity server, no funding round to talk up. What the author stands to gain is credit for a sharp finding, which cuts toward publishing quickly rather than toward distortion. The one party with a reason to push back — Keycloak — is quoted only through its own documentation and has not answered, so the story has no adversarial check on it.
Checkable, not yet checked
We are confident about what was reported and much less confident about how far it generalises. The mechanism hangs together — attribute-based pin, provisioner that writes only consent attributes, condition and executor that never meet on the same event — and each step points at a place in the source where it can be settled. What no one has established is whether this holds beyond 26.7.1 and 26.7.3, whether maintainers regard it as a defect or as intended, and whether a realm with declared default client scopes fares differently.