Before reporting an issue
Area
identity-brokering
Describe the bug
PR #47244 (commit 3e3191d) changed IdentityProviderAuthenticator.authenticate() to read kc_idp_hint from the authentication session client note as a fallback (for PAR support). However, when the IdP callback returns an error and Keycloak re-enters the browser flow via IdentityBrokerService.browserAuthentication(), the client note still contains the original kc_idp_hint value. The authenticator picks it up and re-redirects to the same IdP, creating an infinite loop.
The existing forwardedErrorMessage guard that prevents this loop exists only in the default-provider branch — the kc_idp_hint branch has no such check.
Version
26.6.0
Regression
Expected behavior
When an IdP returns an error (e.g. error=access_denied) during a kc_idp_hint-initiated login, IdentityProviderAuthenticator should call context.attempted() (as it did in 26.5.4), allowing subsequent authenticators in the flow to handle the error - such as showing the login page or redirecting the user back to the RP with an error.
Actual behavior
IdentityProviderAuthenticator reads the persisted kc_idp_hint from the authentication session client note and calls redirect() → context.forceChallenge(), sending the user back to the same failing IdP. This creates an infinite redirect loop.
Subsequent authenticators in the browser flow never execute.
How to Reproduce?
- Configure a browser flow with these ALTERNATIVE executions in order:
- identity-provider-redirector
- auth-username-password-form (or any other authenticator)
- Configure an external IdP (e.g. idp) in the realm
- Initiate a login with kc_idp_hint=idp: /realms/{realm}/protocol/openid-connect/auth?client_id=...&kc_idp_hint=idp
- On the IdP side, return an error: error=access_denied&error_description=User+denied+consent
- Observe that Keycloak redirects back to the IdP instead of falling through to the next authenticator
Anything else?
No response
Before reporting an issue
Area
identity-brokering
Describe the bug
PR #47244 (commit 3e3191d) changed IdentityProviderAuthenticator.authenticate() to read kc_idp_hint from the authentication session client note as a fallback (for PAR support). However, when the IdP callback returns an error and Keycloak re-enters the browser flow via IdentityBrokerService.browserAuthentication(), the client note still contains the original kc_idp_hint value. The authenticator picks it up and re-redirects to the same IdP, creating an infinite loop.
The existing forwardedErrorMessage guard that prevents this loop exists only in the default-provider branch — the kc_idp_hint branch has no such check.
Version
26.6.0
Regression
Expected behavior
When an IdP returns an error (e.g. error=access_denied) during a kc_idp_hint-initiated login, IdentityProviderAuthenticator should call context.attempted() (as it did in 26.5.4), allowing subsequent authenticators in the flow to handle the error - such as showing the login page or redirecting the user back to the RP with an error.
Actual behavior
IdentityProviderAuthenticator reads the persisted kc_idp_hint from the authentication session client note and calls redirect() → context.forceChallenge(), sending the user back to the same failing IdP. This creates an infinite redirect loop.
Subsequent authenticators in the browser flow never execute.
How to Reproduce?
Anything else?
No response