keycloak/keycloak

Error when re-authenticating user with no registered passwordless WebAuthns who has logged in via IdP

Open

#38.902 aberto em 13 de abr. de 2025

Ver no GitHub
 (1 comment) (1 reaction) (0 assignees)Java (8.346 forks)batch import
area/authenticationarea/authentication/webauthnhelp wantedkind/featurepriority/normalteam/core-clients

Métricas do repositório

Stars
 (34.398 stars)
Métricas de merge de PR
 (Mesclagem média 6d 19h) (384 fundiu PRs em 30d)

Description

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:

https://github.com/keycloak/keycloak/blob/195ea98fb29ffde1e88bc6d2006eba8660cbc0d5/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java#L457-L462

It should contain also && !(authenticator instanceof WebAuthnPasswordlessAuthenticator).

and here:

https://github.com/keycloak/keycloak/blob/195ea98fb29ffde1e88bc6d2006eba8660cbc0d5/services/src/main/java/org/keycloak/authentication/authenticators/browser/WebAuthnAuthenticator.java#L91-L97

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.

Guia do colaborador