Error when re-authenticating user with no registered passwordless WebAuthns who has logged in via IdP
#38.902 aperta il 13 apr 2025
Metriche repository
- Star
- (34.398 star)
- Metriche merge PR
- (Merge medio 6g 19h) (384 PR mergiate in 30 g)
Descrizione
Before reporting an issue
- I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.
Area
authentication/webauthn
Describe the bug
I have the following browser authentication flow:
- Cookie [ALTERNATIVE]
- Identity Provider Redirector [ALTERNATIVE] (it actually doesn’t matter if it’s here)
- WebAuthn Passwordless Authenticator [ALTERNATIVE]
and a modified webauthn-authenticate.ftl template that provides a link to log in via IdP (SAML2), so users can either log in with a passwordless passkey or via IdP. This means there may be users who have no registered passkeys.
If the user logs in via IdP and some time later, the client redirects them to the authorization endpoint with max_age=0, i.e. they should be prompted to re-authenticate, but the endpoint returns HTTP 400 and this is logged:
DEBUG [org.keycloak.services.managers.AuthenticationSessionManager] (executor-thread-1) Found AUTH_SESSION_ID cookie with value 972b9f05-d8cd-4d72-be81-e2b2554db2d1
DEBUG [org.keycloak.protocol.AuthorizationEndpointBase] (executor-thread-1) Sent request to authz endpoint. We don't have root authentication session with ID '972b9f
05-d8cd-4d72-be81-e2b2554db2d1' but we have userSession.Re-created root authentication session with same ID. Client is: account-console . New authentication session tab ID: vc8u9f0e99Q
DEBUG [org.keycloak.authentication.AuthenticationProcessor] (executor-thread-1) AUTHENTICATE
DEBUG [org.keycloak.authentication.AuthenticationProcessor] (executor-thread-1) AUTHENTICATE ONLY
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) processFlow: browser passwordless
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) check execution: 'auth-cookie', requirement: 'ALTERNATIVE'
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) authenticator: auth-cookie
DEBUG [org.keycloak.authentication.AuthenticationSelectionResolver] (executor-thread-1) Going through the flow 'browser passwordless' for adding executions
DEBUG [org.keycloak.authentication.AuthenticationSelectionResolver] (executor-thread-1) Selections when trying execution 'auth-cookie' : [ authSelection - auth-cooki
e, authSelection - identity-provider-redirector, authSelection - webauthn-authenticator-passwordless]
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) invoke authenticator.authenticate: auth-cookie
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) authenticator ATTEMPTED: auth-cookie
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) check execution: 'identity-provider-redirector', requirement: 'ALTERNATIVE'
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) authenticator: identity-provider-redirector
DEBUG [org.keycloak.authentication.AuthenticationSelectionResolver] (executor-thread-1) Going through the flow 'browser passwordless' for adding executions
DEBUG [org.keycloak.authentication.AuthenticationSelectionResolver] (executor-thread-1) Selections when trying execution 'identity-provider-redirector' : [ authSelection - identity-provider-redirector]
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) invoke authenticator.authenticate: identity-provider-redirector
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) authenticator ATTEMPTED: identity-provider-redirector
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) check execution: 'webauthn-authenticator-passwordless', requirement: 'ALTERNATIVE'
DEBUG [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-1) authenticator: webauthn-authenticator-passwordless
DEBUG [org.keycloak.authentication.AuthenticationSelectionResolver] (executor-thread-1) Going through the flow 'browser passwordless' for adding executions
DEBUG [org.keycloak.authentication.AuthenticationSelectionResolver] (executor-thread-1) Selections when trying execution 'webauthn-authenticator-passwordless' : []
WARN [org.keycloak.services] (executor-thread-1) KC-SERVICES0013: Failed authentication: org.keycloak.authentication.AuthenticationFlowException
at org.keycloak.authentication.AuthenticationProcessor.authenticateOnly(AuthenticationProcessor.java:1090)
at org.keycloak.authentication.AuthenticationProcessor.authenticate(AuthenticationProcessor.java:917)
at org.keycloak.protocol.AuthorizationEndpointBase.handleBrowserAuthenticationRequest(AuthorizationEndpointBase.java:147)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildAuthorizationCodeAuthorizationResponse(AuthorizationEndpoint.java:380)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.process(AuthorizationEndpoint.java:223)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildGet(AuthorizationEndpoint.java:117)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint$quarkusrestinvoker$buildGet_4b690b27439f19dd29733dc5fd4004f24de0adb6.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:635)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2516)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2495)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1521)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
WARN [org.keycloak.services] (executor-thread-1) KC-SERVICES0013: Failed authentication: org.keycloak.authentication.AuthenticationFlowException: authenticator: webauthn-authenticator-passwordless
at org.keycloak.authentication.DefaultAuthenticationFlow.processSingleFlowExecutionModel(DefaultAuthenticationFlow.java:461)
at org.keycloak.authentication.DefaultAuthenticationFlow.processFlow(DefaultAuthenticationFlow.java:291)
at org.keycloak.authentication.AuthenticationProcessor.authenticateOnly(AuthenticationProcessor.java:1079)
at org.keycloak.authentication.AuthenticationProcessor.authenticate(AuthenticationProcessor.java:917)
at org.keycloak.protocol.AuthorizationEndpointBase.handleBrowserAuthenticationRequest(AuthorizationEndpointBase.java:147)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildAuthorizationCodeAuthorizationResponse(AuthorizationEndpoint.java:380)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.process(AuthorizationEndpoint.java:223)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildGet(AuthorizationEndpoint.java:117)
at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint$quarkusrestinvoker$buildGet_4b690b27439f19dd29733dc5fd4004f24de0adb6.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:635)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2516)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2495)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1521)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
I’m running Keycloak 26.1.5, but I’m quite sure this can be reproduced on the last version as well.
Version
26.1.5
Regression
- The issue is a regression
Expected behavior
The user should be provided with the webauthn login form and “Please re-authenticate to continue”.
Actual behavior
Described above.
How to Reproduce?
Described above. Another way how to reproduce it is to log in, wait >5 min, open the account console and try to add a Passkey or TOTP.
Anything else?
The problem is in this condition:
It should contain also && !(authenticator instanceof WebAuthnPasswordlessAuthenticator).
and here:
It assumes that this branch is called only in 2nd factor scenario, but in fact, it’s called also in the passwordless scenario when re-authenticating the user. I fixed it by overriding the authenticate() method in WebAuthnPasswordlessAuthenticator and making this branch no-op.