Skip to content

tut: Build a custom Keycloak authentication flow - #395

Merged
xgp merged 2 commits into
mainfrom
content/T33-custom-authentication-flow
Sep 17, 2026
Merged

xgp merged 2 commits into
mainfrom
content/T33-custom-authentication-flow

Conversation

@xgp

@xgp xgp commented Sep 14, 2026

Copy link
Copy Markdown
Member

Build a Custom Keycloak Authentication Flow

Cluster: D (concepts/how-to) · Queue item: T33 · Track: Authentication (new)
Primary keyword: keycloak custom authentication flow
Secondary: keycloak conditional authentication, keycloak conditional otp, keycloak authentication flow requirement, keycloak step up authentication flow

What this helps a reader do

Build and bind a working conditional authentication flow — users holding one role must
present a TOTP code, everyone else logs in with a password only, and SSO keeps working —
and then recognise the ways the requirement model fails without producing an error.

The official documentation explains what REQUIRED, ALTERNATIVE, CONDITIONAL, and
DISABLED mean. It does not say what to set, in what order, or what happens when you get it
wrong, and every one of those failures is silent: the flow saves, loads, and logs people in
while doing something other than what was intended. This page links the official reference
rather than restating it, and supplies the tested kcadm.sh sequence, the condition
decision table, and the named failure modes.

Verified against

Keycloak 26.7.3 (quay.io/keycloak/keycloak:26.7.3, start-dev, H2 dev database),
in a container on the drafting machine. Every command, error string, and login result in the
page was produced by an actual run, and the full published command sequence was re-run
verbatim against a fresh realm at the end.

Measured results reproduced in the page:

  • Role-holder with TOTP enrolled → 1 OTP prompt; role-holder without → forced to
    execution=CONFIGURE_TOTP; non-role-holder → 0 prompts, straight to code=.
  • Leaving the copied Browser - Conditional 2FA subflow enabled next to a second
    conditional 2FA subflow → 2 OTP prompts for the same user. Disabling it → 1.
  • Setting the forms subflow to REQUIRED at the top level stops the Cookie
    authenticator running, so the second app visit shows a login form instead of an
    immediate code=. Reverting restores SSO.
  • A CONDITIONAL subflow containing no condition execution is skipped entirely — the
    REQUIRED OTP form inside it never runs, and nothing is written to the server log.
  • Executions created via kcadm arrive DISABLED.
  • kcadm update .../executions -n -s requirement=… sends a partial representation and
    Keycloak stores priority: 0, moving the execution to the front of its level. The page
    uses a read-modify-write helper instead; priorities were confirmed preserved.
  • Error strings quoted verbatim: It is illegal to add sub-flow to a built in flow and
    ModelException: Cannot remove authentication flow, it is currently in use.

The condition decision table is generated from authentication/config-description/<id> on
26.7.3, so the help text and config keys are the server's own.

Could not verify

  • Admin-console click path. The <details> block was written from the REST behaviour
    and the 26.7.3 console layout, not from a click-through — this run had no browser.
  • conditional-level-of-authentication and conditional-sub-flow-executed are
    described from their server-supplied help text and config keys only; neither was exercised
    in a login. They are named as pointers, and no behavioural claim is made about them.
  • Tested on the dev-mode H2 database only. Nothing here is storage-dependent, but it was not
    run against a production database.

Structure notes

  • Adds a new Authentication track to sidebars.tutorials.js — this is its first
    tutorial, so the category lands with it rather than as an empty dead end.
  • Adds a "Customising the login experience" learning path to tutorials/index.md.
  • No hero image, and no image: frontmatter key.

Internal links added

  • /tutorials/getting-started/first-realm-client-user/ (sibling)
  • /tutorials/getting-started/run-keycloak-locally/ (sibling)
  • /tutorials/securing-applications/validating-tokens/ (sibling)
  • /docs/authentication/understanding-flows/ (docs)
  • /docs/authentication/complex-flows/ (docs)
  • /hosting/dedicated-clusters/, /support/, /extensions/ via <ProductionNote />, which
    is the only Phase Two mention in the page

Follow-ups for later PRs

  • Link this page from the magic-link and email-OTP tutorials once those land; both build on
    the same conditional-subflow mechanics and neither exists on main yet.
  • A dedicated step-up-authentication page (ACR/LOA) is a natural spoke — this page
    deliberately stops at naming conditional-level-of-authentication rather than covering it.
  • docs/authentication/understanding-flows.md could gain a pointer back to this tutorial.

Checklist

  • pnpm install --frozen-lockfile && pnpm build passes
  • Keyword in title, description, and first 150 words
  • One extractable direct-answer block (the four-step procedure in the opening)
  • ≥1 canonical commercial link, ≥2 sibling links, ≥1 docs link
  • Every code block tested against 26.7.3
  • Tutorial is vendor-neutral; Phase Two appears only in <ProductionNote />
  • Hero image (none — omitted deliberately)

Rationale and queue state live in the private content-marketing repo.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Deploying phasetwo-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 407986d
Status:🚫  Build failed.

View logs

@xgp
xgp marked this pull request as ready for review September 14, 2026 09:14
@xgp
xgp requested a review from pnzrr September 14, 2026 09:14
xgp and others added 2 commits September 17, 2026 14:09
Building a flow with kcadm mutates a bound flow one call at a time, which is
fine on a throwaway realm and wrong on one serving logins. Add a "Doing this
safely in production" section naming the window that opens between calls, and
point at the Apache 2.0 keycloak-atomic-auth-flows extension and its
announcement post as the way to apply a whole flow tree in one transaction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GUJfhmq5sERGKP3o2tFcKs
@xgp
xgp force-pushed the content/T33-custom-authentication-flow branch from 8414016 to 407986d Compare September 17, 2026 13:11
@xgp
xgp merged commit 2455569 into main Sep 17, 2026
2 of 3 checks passed
@xgp
xgp deleted the content/T33-custom-authentication-flow branch September 17, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants