the `http challenge` flow removal, narrowed to 22.0.0
- Host
- local laptop Docker (Maven container); no Keycloak server, no DB
- Condition
- Narrow the http challenge flow removal to a release (debt from the rich-ladder run)
Why this run existed
2026-08-26-rich-21-realm-ladder-100k observed that the built-in
http challenge authentication flow — present on the 21.1.2 realm — was
absent on 26.7.1, and left two rows open: which hop removed it, and what
happens to a custom flow that references it. Neither needed a box: the flow
definition and the migration logic are in the published artifacts, so this run
is pure jar inspection against the .m2 cache.
Environment
| Field | Value |
|---|---|
| Artifacts inspected | keycloak-server-spi-private (flow definitions), keycloak-model-legacy-private / keycloak-model-storage-private (migrators) |
| Versions | 21.1.2, 22.0.0, 22.0.1–22.0.5, 23.0.7, 24.0.5, 25.0.6, 26.0.0–26.7.1 |
| Method | javap / jar tf / strings on DefaultAuthenticationFlows and MigrateTo22_0_0 |
| Host | local laptop Docker (Maven container); no Keycloak server, no DB |
Findings
Finding 1 — the flow definition was deleted at 22.0.0 ⭐
org.keycloak.models.utils.DefaultAuthenticationFlows
(keycloak-server-spi-private) carries the built-in flow definitions. At
21.1.2 it has:
public static final String HTTP_CHALLENGE_FLOW; // = "http challenge"
public static void httpChallengeFlow(RealmModel); // adds the flow + executions
and both addFlows(RealmModel) and migrateFlows(RealmModel) call
httpChallengeFlow(realm) — i.e. the flow is created for new realms and
re-added to existing realms during migration.
At 22.0.0 the constant and the method are gone from the class, and every version 22.0.0 → 26.7.1 lacks them:
| Version | HTTP_CHALLENGE_FLOW + httpChallengeFlow() |
|---|---|
| 21.1.2 | present |
| 22.0.0 | absent |
| 22.0.1 … 22.0.5, 23.0.7, 24.0.5, 25.0.6, 26.x | absent |
The flow's constituent authenticators were not removed, only re-homed:
http-basic-authenticator moved into samlEcpProfile (SAML ECP) and
docker-http-basic-authenticator into dockerAuthenticationFlow. The top-level
"http challenge" flow container itself is what disappeared.
Finding 2 — a new migrator actively removes the flow from existing realms, at 22.0.0 ⭐⭐
The flow definition removal alone would leave an upgraded realm's existing
"http challenge" flow in place. Keycloak closes that gap with a dedicated
migration step, org.keycloak.migration.migrators.MigrateTo22_0_0, which is
introduced at 22.0.0 (present in keycloak-model-legacy-private 22.0.0, absent
at 21.1.2; lives in keycloak-model-storage-private from 24+).
Its removeHttpChallengeFlow(KeycloakSession, RealmModel) method:
- looks the flow up by alias —
realm.getFlowByAlias("http challenge"); - if present, calls
KeycloakModelUtils.deepDeleteAuthenticationFlow(session, realm, flow, onRemoved, onInUse, flow.isBuiltIn()).
The two callbacks encode two distinct outcomes, both visible in the migration log:
- not in use → the flow is deleted, and a DEBUG line is logged:
Removed 'http challenge' authentication flow in realm '<realm>'. - in use (referenced by a client's flow-binding override or another flow) →
the flow is left in place and an ERROR is logged:
Authentication flow 'http challenge' is in use in realm '<realm>' and cannot be removed. Please update your deployment to avoid using this flow before migration to latest Keycloak.
This resolves the open "custom flow that references it" question: the removal is not silent data loss. A referenced flow survives the migration as realm data, but Keycloak emits an ERROR and stops maintaining the flow — the reference is left dangling against a definition that no longer exists. The operator must re-write the custom flow before (or at) the 22.0.0 hop.
Finding 3 — the release boundary is the 21 → 22 hop, not any later one ⭐
Both halves (definition deleted + migrator added) land at 22.0.0, so the removal is squarely the 21.1.2 → 22.0.0 hop. The rich-ladder run's six hops started at 21.1.2 and could not see where it happened; the answer is the first hop.
Verification
| Claim | Primary source | Checked |
|---|---|---|
httpChallengeFlow + HTTP_CHALLENGE_FLOW present at 21.1.2 | javap of DefaultAuthenticationFlows 21.1.2 | ☑ |
| Both absent at 22.0.0 and every later cached version | strings/javap across 22.0.0 … 26.7.1 | ☑ |
addFlows and migrateFlows both called it at 21.1.2 | javap -c call sites | ☑ |
MigrateTo22_0_0 introduced at 22.0.0 | present in keycloak-model-legacy-private 22.0.0, absent at 21.1.2 | ☑ |
| It removes the flow by alias, two outcomes | javap -c of removeHttpChallengeFlow + log strings | ☑ |
http-basic-authenticator re-homed, not removed | 22.0.0 DefaultAuthenticationFlows: referenced by samlEcpProfile / dockerAuthenticationFlow | ☑ |
Publishable extract
One specific Keycloak built-in flow dies on the 21 → 22 upgrade, and it dies loudly only if you are using it.
Keycloak 22.0.0 removed the built-in
http challengeauthentication flow. The flow definition and the code that created it are gone, and a dedicated migration step (MigrateTo22_0_0) deletes the flow from every realm on the way up. If your realm does not reference it, it is removed silently — a DEBUG line, nothing more. If a custom flow or client still references it, Keycloak leaves it in place and logs an ERROR telling you to update your deployment before migrating.The constituent HTTP Basic authenticators were not removed — they moved into the SAML ECP and docker-auth flows. What vanished is the standalone "http challenge" flow, and the place that matters is the 21 → 22 hop, not any later upgrade.