Skip to content

Handle orphan authenticator providers in flows - #48372

Open
erenkan wants to merge 2 commits into
keycloak:mainfrom
erenkan:orphan-authenticator-executions
Open

Handle orphan authenticator providers in flows#48372
erenkan wants to merge 2 commits into
keycloak:mainfrom
erenkan:orphan-authenticator-executions

Conversation

@erenkan

@erenkan erenkan commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Closes #15535

Problem

An authentication flow execution stores only its provider id string. If the referenced SPI is uninstalled, renamed, or fails to register, GET /admin/realms/{realm}/authentication/flows/{alias}/executions throws NotFoundException("Could not find authenticator provider"). The 404 propagates to the Admin UI's global error boundary and the entire flow page crashes — admins cannot delete the orphan execution, reorder siblings, or edit anything else in the flow.

Screen.Recording.2026-04-22.at.17.22.08.mov

@erenkan
erenkan requested review from a team as code owners April 22, 2026 14:28
@erenkan
erenkan force-pushed the orphan-authenticator-executions branch from 0dca0fc to 7125f1e Compare April 22, 2026 14:46
@erenkan

erenkan commented Apr 22, 2026

Copy link
Copy Markdown
Contributor Author

Quick question for maintainers on the copyright header of the new test file OrphanExecutionTest.java:

I removed the Copyright YYYY Red Hat, Inc. and/or its affiliates line and kept only the Apache 2.0 license boilerplate, since:

  • Keycloak uses Apache 2.0 + DCO; copyright stays with the contributor, not assigned to the project.
  • ASF's own policy recommends no per-file copyright notices; attribution goes in a project-level NOTICE file.
  • As an external contributor I can't legally speak on Red Hat's behalf, but writing my own company name would deviate from the convention every other file in the package follows.

What's your preference?

  1. Leave it as-is (no copyright line, Apache 2.0 header only)
  2. Add Copyright 2026 Red Hat, Inc. and/or its affiliates to match project convention
  3. Add my own (Copyright 2026 Keymate Inc. / my name)

Happy to push whichever you prefer.

@erenkan
erenkan force-pushed the orphan-authenticator-executions branch from 7125f1e to aa83bbd Compare June 12, 2026 14:20

@keycloak-github-bot keycloak-github-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot

Copy link
Copy Markdown

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.saml.SamlClientTest#testLoginWithOIDCClient

Keycloak CI - Java Distribution IT (windows-latest - temurin - 21)

jakarta.ws.rs.ClientErrorException: HTTP 409 Conflict
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:258)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.DefaultEntityExtractorFactory$3.extractEntity(DefaultEntityExtractorFactory.java:41)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:146)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:104)
...

Report flaky test

org.keycloak.testsuite.saml.SamlClientTest#testLoginWithOIDCClient

Keycloak CI - Java Distribution IT (windows-latest - temurin - 17)

jakarta.ws.rs.ClientErrorException: HTTP 409 Conflict
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:258)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.DefaultEntityExtractorFactory$3.extractEntity(DefaultEntityExtractorFactory.java:41)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:146)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:104)
...

Report flaky test

org.keycloak.testsuite.saml.SamlClientTest#testLoginWithOIDCClient

Keycloak CI - Java Distribution IT (ubuntu-latest - temurin - 17)

jakarta.ws.rs.ClientErrorException: HTTP 409 Conflict
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:258)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.DefaultEntityExtractorFactory$3.extractEntity(DefaultEntityExtractorFactory.java:41)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:146)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:104)
...

Report flaky test

org.keycloak.testsuite.saml.SamlClientTest#testLoginWithOIDCClient

Keycloak CI - Java Distribution IT (ubuntu-latest - temurin - 21)

jakarta.ws.rs.ClientErrorException: HTTP 409 Conflict
	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:258)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.DefaultEntityExtractorFactory$3.extractEntity(DefaultEntityExtractorFactory.java:41)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:146)
	at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:104)
...

Report flaky test

org.keycloak.testsuite.adapter.servlet.SAMLServletAdapterTest#testSavedPostRequest

Keycloak CI - Adapter IT Strict Cookies

java.lang.AssertionError: 

Expected: a string ending with "secured/post"
     but: was "/auth/realms/demo/login-actions/authenticate"
	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
...

Report flaky test

Copilot AI review requested due to automatic review settings June 18, 2026 07:25
@erenkan
erenkan force-pushed the orphan-authenticator-executions branch from aa83bbd to a4f55e2 Compare June 18, 2026 07:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes authentication flow management resilient to “orphan” executions—executions that reference an authenticator provider ID whose factory is no longer registered—so the Admin UI and Admin REST API can still list, reorder (within the same flow), and delete those executions instead of failing with a 404.

Changes:

  • Admin REST: return a placeholder execution representation (including a new providerUnavailable flag) instead of throwing NotFoundException when the authenticator factory is missing.
  • Admin UI: surface a “Provider unavailable” indicator and prevent operations that would implicitly re-create the execution in a different flow (cross-flow move) or edit configuration.
  • Tests & client types: add an integration test covering listing/removal and extend the admin-client TS type with providerUnavailable.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/base/src/test/java/org/keycloak/tests/admin/authentication/OrphanExecutionTest.java Adds coverage ensuring orphan executions are still listed and can be removed via admin API.
services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java Stops throwing on missing authenticator factory during execution listing; emits a placeholder rep and sets providerUnavailable.
core/src/main/java/org/keycloak/representations/idm/AuthenticationExecutionInfoRepresentation.java Adds providerUnavailable to the execution info representation.
js/libs/keycloak-admin-client/src/defs/authenticationExecutionInfoRepresentation.ts Extends the TS definition to include providerUnavailable?: boolean.
js/apps/admin-ui/src/authentication/FlowDetails.tsx Blocks cross-flow move for orphan-containing nodes (to avoid delete + re-create losing rows) and makes child moves sequential.
js/apps/admin-ui/src/authentication/components/FlowTitle.tsx Displays a warning label and help text when providerUnavailable is true.
js/apps/admin-ui/src/authentication/components/FlowRow.tsx Hides the execution config modal trigger for orphan executions.
js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties Adds i18n strings for the “provider unavailable” UI messaging.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Copilot AI review requested due to automatic review settings June 25, 2026 12:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java:763

  • providerId is Base32-encoded unconditionally for script- providers, even when factory == null (orphan execution). That breaks the stated goal of preserving the original provider id for orphaned executions, and can make the orphan fallback inconsistent (displayName is set from the raw id earlier, but providerId becomes encoded here). Consider only encoding when a factory is present (i.e., the provider is actually available) and keep the raw value for orphan placeholders.
                // encode the provider id in case the provider is a script deployed to the server to make sure it can be used as path parameters without break the URL syntax
                if (providerId.startsWith("script-")) {
                    providerId = Base32.encode(providerId.getBytes());
                }

erenkan and others added 2 commits July 19, 2026 16:13
The admin REST endpoint that lists flow executions threw NotFoundException
when an execution referenced a provider id with no registered factory (SPI
uninstalled/renamed/failed to register). The Admin UI propagated the 404
to its global error boundary, making the whole flow page uneditable — the
admin could not delete the orphan execution or manage the rest of the flow.

Backend: populate a placeholder AuthenticationExecutionInfoRepresentation
with providerUnavailable=true when the factory lookup returns null, so
listing keeps working. AuthenticationExecutionInfoRepresentation gains an
optional providerUnavailable Boolean.

Admin UI: FlowTitle shows a red "Provider unavailable" badge, FlowRow
hides the gear icon, and cross-flow drag is rejected up-front (including
when an orphan is nested inside a sub-flow being moved) so the delete +
re-create sequence never silently drops the row. Same-flow reorder and
delete remain functional.

Closes keycloak#15535

Signed-off-by: erenkan <eren@keymate.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Eren Kan <erenkn@gmail.com>
Copilot AI review requested due to automatic review settings July 19, 2026 13:13
@erenkan
erenkan force-pushed the orphan-authenticator-executions branch from 750b456 to a4c5975 Compare July 19, 2026 13:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fail more gracefully when AuthenticatorProvider is missing

3 participants