+
Skip to content

After upgrade to 26.3: Not possible to use Credentials having not-unique label #41945

@s76527

Description

@s76527

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

login/ui

Describe the bug

If a user previously had multiple Credentials registered and some of them had duplicated labels, such users can't use these Credentials to authenticate themselves with Keycloak 26.3 anymore.

Version

26.3.2

Regression

  • The issue is a regression

Expected behavior

Allow using Credentials with duplicated labels from the past or provide a mechanism to resolve such duplicates.

Actual behavior

Using such a Credential to authenticate causes Exception:

2025-08-18 13:39:14,451 WARN  [org.keycloak.services] (executor-thread-20) KC-SERVICES0013: Failed authentication: org.keycloak.models.ModelDuplicateException: Device already exists with the same name
	at org.keycloak.models.jpa.JpaUserCredentialStore.validateDuplicateCredential(JpaUserCredentialStore.java:151)
	at org.keycloak.models.jpa.JpaUserCredentialStore.updateCredential(JpaUserCredentialStore.java:66)
	at org.keycloak.models.jpa.JpaUserProvider.updateCredential(JpaUserProvider.java:891)
	at org.keycloak.credential.UserCredentialManager.updateStoredCredential(UserCredentialManager.java:104)
	at org.keycloak.models.cache.infinispan.SubjectCredentialManagerCacheAdapter.updateStoredCredential(SubjectCredentialManagerCacheAdapter.java:47)
	at org.keycloak.credential.WebAuthnCredentialProvider.isValid(WebAuthnCredentialProvider.java:226)
	at org.keycloak.credential.UserCredentialManager.lambda$validate$11(UserCredentialManager.java:274)
	at org.keycloak.tracing.NoopTracingProvider.trace(NoopTracingProvider.java:64)
	at org.keycloak.tracing.NoopTracingProvider.trace(NoopTracingProvider.java:74)
	at org.keycloak.credential.UserCredentialManager.lambda$validate$12(UserCredentialManager.java:273)
	at java.base/java.util.Collection.removeIf(Collection.java:576)
	at org.keycloak.credential.UserCredentialManager.validate(UserCredentialManager.java:271)
	at org.keycloak.credential.UserCredentialManager.lambda$isValid$0(UserCredentialManager.java:77)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1779)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at org.keycloak.credential.UserCredentialManager.isValid(UserCredentialManager.java:77)
	at org.keycloak.models.SubjectCredentialManager.isValid(SubjectCredentialManager.java:45)
	at org.keycloak.authentication.authenticators.browser.WebAuthnAuthenticator.action(WebAuthnAuthenticator.java:242)
	at org.keycloak.authentication.authenticators.browser.WebAuthnPasswordlessAuthenticator.action(WebAuthnPasswordlessAuthenticator.java:115)
	at org.keycloak.authentication.DefaultAuthenticationFlow.processAction(DefaultAuthenticationFlow.java:149)
	at org.keycloak.authentication.AuthenticationProcessor.authenticationAction(AuthenticationProcessor.java:1064)
	at org.keycloak.services.resources.LoginActionsService.processFlow(LoginActionsService.java:378)
	at org.keycloak.services.resources.LoginActionsService.processAuthentication(LoginActionsService.java:349)
	at org.keycloak.services.resources.LoginActionsService.authenticate(LoginActionsService.java:341)
	at org.keycloak.services.resources.LoginActionsService.authenticateForm(LoginActionsService.java:407)
	at org.keycloak.services.resources.LoginActionsService$quarkusrestinvoker$authenticateForm_8a5eee1a0ec5f9d46c9be1d4352061fa6806b300.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$15.runWith(VertxCoreRecorder.java:638)
	at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
	at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
	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:840)

In UI it looks like:

Image

How to Reproduce?

  1. Setup a user in Keycloak prior to v26.3 like this:
Image
  1. Upgrade to Keycloak 26.3
  2. Try to use a Credential with duplicated label at Login

Anything else?

The migration guide for v26.3.0 mentions:

Different credentials of a user need to have different names

When adding an OTP, WebAuthn or any other 2FA credentials, the name the user assigns to this credential needs to be unique for the given user. This allows the user to distinguish between those credentials, and either update or delete them later. If a user tries to create a credential with an already existing name, there is an error message and the user is asked to change the name of the new credential.

But it doesn't mention that even existing labels are affected by this change.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载