The Keycloak Upgrade Ledger

Every upgrade we have rehearsed, with the environment stated and the clock running.

2026-08-26 · run 2026-08-26-http-challenge-flow-removal-narrowed

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

FieldValue
Artifacts inspectedkeycloak-server-spi-private (flow definitions), keycloak-model-legacy-private / keycloak-model-storage-private (migrators)
Versions21.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
Methodjavap / jar tf / strings on DefaultAuthenticationFlows and MigrateTo22_0_0
Hostlocal 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:

VersionHTTP_CHALLENGE_FLOW + httpChallengeFlow()
21.1.2present
22.0.0absent
22.0.1 … 22.0.5, 23.0.7, 24.0.5, 25.0.6, 26.xabsent

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:

  1. looks the flow up by alias — realm.getFlowByAlias("http challenge");
  2. 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:

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

ClaimPrimary sourceChecked
httpChallengeFlow + HTTP_CHALLENGE_FLOW present at 21.1.2javap of DefaultAuthenticationFlows 21.1.2
Both absent at 22.0.0 and every later cached versionstrings/javap across 22.0.0 … 26.7.1
addFlows and migrateFlows both called it at 21.1.2javap -c call sites
MigrateTo22_0_0 introduced at 22.0.0present in keycloak-model-legacy-private 22.0.0, absent at 21.1.2
It removes the flow by alias, two outcomesjavap -c of removeHttpChallengeFlow + log strings
http-basic-authenticator re-homed, not removed22.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 challenge authentication 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.

← Back to the Ledger