| Filename | Latest commit message | Latest commit date |
|---|---|---|
Adds a keycloak_auth_flow role that locally validates flow definitions (structure, nesting depth, known provider IDs, and a set of Keycloak authentication-flow gotchas: conditions silently ignored outside a Conditional subflow, keycloak/keycloak#29515's OTP+WebAuthn sibling bug, conditional-credential config shape/semantics, and more) before deploying them idempotently via middleware_automation.keycloak.keycloak_authentication_v2. Includes the CCCHH realm's actual "browser passkey and token" flow (the realm's real browserFlow binding, not the untouched built-in "browser" flow), exported from the live server so it's now reviewable and redeployable from this repo instead of only editable in the Keycloak admin console. |
||
| .. | ||
| defaults | ||
| library | ||
| meta | ||
| tasks | ||
| README.md | ||
keycloak_auth_flow role
Manages Keycloak authentication flows (for example a realm's browser flow) as YAML checked
into this repo, instead of hand-editing them in the Keycloak admin console.
Each flow is validated locally (structure, nesting depth, known provider IDs) with the custom
keycloak_auth_flow_lint module before being deployed with
middleware_automation.keycloak.keycloak_authentication_v2, which applies changes idempotently
via a "safe swap" so the realm is never left without a working flow mid-update.
Both tasks run against keycloak_auth_flow__admin_url, which defaults to the local
http://127.0.0.1:8080 — i.e. this role is meant to run on the Keycloak host itself, talking to
the Keycloak container directly, since the public admin hostname is IP-restricted.
Required Arguments
keycloak_auth_flow__admin_password: Password ofkeycloak_auth_flow__admin_username(an admin in themasterrealm).
Optional Arguments
keycloak_auth_flow__flows: List of flow definitions to deploy. Defaults to[]. Each item:realm: Realm the flow belongs to.alias: Name of the flow.providerId:basic-flow(default) orclient-flow.description: Optional human-readable description.authenticationExecutions: Nested list describing the flow's steps, in the shape documented bymiddleware_automation.keycloak.keycloak_authentication_v2— each item is either an execution (providerId,requirement, optionallyauthenticationConfig) or a sub-flow (subFlow,requirement, optionallysubFlowType, and a nestedauthenticationExecutions). Up to 10 levels of sub-flow nesting are supported.
keycloak_auth_flow__admin_url: Defaults tohttp://127.0.0.1:8080.keycloak_auth_flow__admin_username: Defaults toadmin.
Validation checks (keycloak_auth_flow_lint)
The lint module runs entirely offline (no Keycloak credentials or network access needed) and checks each flow definition before it's ever sent to the server.
Errors (fail the run)
- Node shape: every entry in
authenticationExecutionsmust be a mapping with no unknown keys, exactly one ofproviderId(an execution) orsubFlow(a sub-flow), and arequirementthat is one ofREQUIRED/ALTERNATIVE/DISABLED/CONDITIONAL. A sub-flow'ssubFlowType(when given) must bebasic-floworform-flow, its name must be a non-empty string unique within the flow, and it must define at least one child execution. These mirror the shapemiddleware_automation.keycloak.keycloak_authentication_v2itself expects — see its module source. - Nesting depth: at most 10 levels of sub-flow nesting, matching the limit built into
keycloak_authentication_v2(see the module source linked above; the depth was raised from 4 to 10 in ansible-middleware/keycloak#376). authenticationConfigshape: when present, must be a mapping with onlyalias(non-empty string) andconfig(a mapping) — again mirrors whatkeycloak_authentication_v2accepts.- Condition execution outside a
CONDITIONALsub-flow: aconditional-*execution is silently ignored — no error, the condition just never fires — unless its immediate parent sub-flow's ownrequirementis literallyCONDITIONAL. See the Keycloak Server Admin Guide, "Configuring authentication → Execution requirements": "Condition executions can only be contained in Conditional subflow." - OTP + WebAuthn siblings under one shared
conditional-user-configured: this hits keycloak/keycloak#29515 — users with neither credential configured get a generic login error instead of the check being skipped (also discussed in keycloak/keycloak#14988). The fix is to give each credential type its own nestedCONDITIONALsub-flow with its ownconditional-user-configuredcheck. conditional-credentialconfig: requires anauthenticationConfigwith a non-emptycredentialsentry, andincluded(if set) must be the string"true"or"false"— Keycloak stores authenticator config values as strings. SeeConditionalCredentialAuthenticatorFactoryjavadoc and conditions.adoc.
Warnings (non-fatal)
- Unrecognized
providerId: not in the module's built-in list of known Keycloak provider IDs — could be a typo, or a legitimate custom SPI provider. See the constantKNOWN_PROVIDER_IDSin the module source for how that list was compiled and how to check or extend it (live server query vs. Keycloak's authenticator factory sources). CONDITIONALrequirement on a non-conditional-*provider: usually a sign the wrong execution ended up under aConditionalsub-flow.conditional-credentialincludedsemantics reminder: restates what the configuredincludedvalue actually means (true→ condition is true when any listed credential was used;false→ true when none were used). This is unlabeled in the admin console and easy to get backwards — see the same conditions.adoc reference as above.auth-conditional-otp-formwith no config: unlike the plainauth-otp-form,Conditional OTP Form's skip/force logic is entirely self-contained (by user attribute, role, header, or default) and does not inherit a wrappingCondition-*check; with no config it defaults to always showing the OTP form. SeeConditionalOtpFormAuthenticator.java.- Ambiguous passkey execution: when both
auth-username-password-formandwebauthn-authenticator-passwordlessappear anywhere in the flow. With native passkey support, the username/password form can itself complete a full passkey login, so it can be unclear — and unverifiable from the admin console — which execution actually authenticated a given login. No online source for this one; verify via theexecution=<id>query parameter during a live test, orDEBUGlogging onorg.keycloak.authentication.
Example
keycloak_auth_flow__flows:
- realm: ccchh
alias: browser passkey and token
providerId: basic-flow
authenticationExecutions:
- providerId: auth-cookie
requirement: ALTERNATIVE
- subFlow: forms
requirement: ALTERNATIVE
authenticationExecutions:
- providerId: auth-username-password-form
requirement: REQUIRED