Skip to content

Module to manage keycloak authentication flows in an idempotent way #11556

@thomasbargetz

Description

@thomasbargetz

Summary

I would like to propose a new module — keycloak_authentication_v2 — for managing Keycloak authentication flows in a fully declarative and idempotent way, including safe updates for flows that are actively in use.


Motivation

The existing keycloak_authentication module covers a range of use cases well, but does not address the specific scenario of fully declarative, whole-flow management with safe in-place updates. The goal of this feature request is a module that treats an authentication flow as a complete, self-contained unit of configuration: you declare the entire desired state, and the module converges the current state to match it — creating, updating, or deleting the flow as needed, without any side effects.

Specifically, the module should:

  • Define a flow as a whole. The entire flow — including all executions, sub-flows, and their configurations — is declared in one place. If the declared state differs from what exists in Keycloak, the module updates the flow to match exactly.
  • Support deletion of executions and sub-flows. If an execution or sub-flow is present in Keycloak but absent from the declared state, it should be removed.
  • Support safe updates for flows that are in use. When a flow is actively bound to the realm (e.g. as the default browser flow or direct grant flow) or used as a per-client authentication flow override, updating it in-place is unsafe. The module should handle this transparently via the mechanism described below.

Proposed Safe Swap Mechanism

Updating an active flow in-place introduces a race condition. Consider a flow that validates a password: if the password execution is removed before a replacement (e.g. a custom password check execution) is added, there is a time window during which users can authenticate without any credential validation.

To eliminate this risk, the module should use a Safe Swap procedure:

  1. Create the fully configured new flow under a temporary alias (e.g. myflow_tmp_for_swap).
  2. Re-point all realm bindings and client overrides from the old flow to the new temporary flow.
  3. Delete the old flow.
  4. Rename the temporary flow back to the original alias.

This ensures that a fully-formed flow is always in place at every point during the update. No intermediate state is reachable by end users.


Why the existing keycloak_authentication module does not meet these requirements

The existing module was not designed for declarative, whole-flow management, and extending it to support this use case would require changes that are fundamentally incompatible with its current behaviour:

  1. Partial updates instead of declarative management. The existing module can add executions to a flow without the caller declaring the flow in its entirety. This means executions or sub-flows that are present in Keycloak but absent from the task definition are left untouched — they are never removed. Declarative management, by contrast, requires that the live state converges exactly to the declared state, including the removal of undeclared executions.

  2. Copy-based creation model. As stated in the module documentation, the module's creation model is based on copying an existing flow and then appending executions to it. This is at odds with the declarative philosophy, where a flow is defined from scratch rather than derived from another. Modelling a new module on this approach would require users to maintain a dependency on a source flow

Both of these limitations are inherent to the existing module's design. Addressing them inside the current module would break its documented behaviour and is therefore more appropriately addressed in a new module.


Proposed module: keycloak_authentication_v2

A reference implementation is available and attached to this issue. Key characteristics:

  • Fully declarative: the entire flow is defined in the task, and the module converges to that exact state.
  • Idempotent: if the live flow already matches the declared state, no changes are made.
  • Safe Swap for active flows: flows bound to the realm or to client overrides are updated without any unsafe intermediate state.
  • Full check_mode and diff_mode support.

Checklist

  • Reference implementation provided
  • Documentation written (DOCUMENTATION, EXAMPLES, RETURN blocks)
  • check_mode supported
  • diff_mode supported
  • Idempotency verified manually against a running Keycloak instance

Issue Type

Feature Idea

Component Name

keycloak_authentication_v2

Additional Information

- name: Create or modify the 'My Login Flow'.
  community.general.keycloak_authentication_v2:
    auth_keycloak_url: http://localhost:8080/auth
    auth_realm: master
    auth_username: admin
    auth_password: password
    realm: master
    alias: "My Login Flow"
    authenticationExecutions:
      - providerId: idp-review-profile
        requirement: REQUIRED
        authenticationConfig:
          alias: My Login Flow - review profile config
          config:
            update.profile.on.first.login: "missing"
      - subFlow: My Login Flow - User creation or linking
        requirement: REQUIRED
        authenticationExecutions:
          - providerId: idp-create-user-if-unique
            requirement: ALTERNATIVE
            authenticationConfig:
              alias: My Login Flow - create unique user config
              config:
                require.password.update.after.registration: "true"
          - providerId: auth-cookie
            requirement: REQUIRED
          - subFlow: My Login Flow - Handle Existing Account
            requirement: ALTERNATIVE
            authenticationExecutions:
              - providerId: idp-confirm-link
                requirement: REQUIRED
      - providerId: auth-cookie
        requirement: DISABLED
    state: present

Code of Conduct

  • I agree to follow the Ansible Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureThis issue/PR relates to a feature requesthas_prmodulemodulepluginsplugin (any type)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions