keycloak/keycloak

Client-initiated account linking fails when user already has a federated identity

Open

#35,059 创建于 2024年11月18日

在 GitHub 查看
 (26 评论) (17 反应) (2 负责人)Java (8,346 fork)batch import
area/identity-brokeringhelp wantedkind/bugpriority/normalteam/core-iam

仓库指标

Star
 (34,398 star)
PR 合并指标
 (平均合并 6天 19小时) (30 天内合并 384 个 PR)

描述

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

identity-brokering

Describe the bug

Attempting to link an identity provider account via client-initiated account linking fails when a user already has a federated identity from the provider, throwing this exception:

keycloak-1  | 2024-11-18 18:44:59,608 INFO  [org.hibernate.orm.jdbc.batch] (executor-thread-1) HHH100503: On release of batch it still contained JDBC statements
keycloak-1  | 2024-11-18 18:44:59,609 ERROR [org.hibernate.orm.jdbc.batch] (executor-thread-1) HHH100501: Exception executing batch [org.h2.jdbc.JdbcBatchUpdateException: Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_40 ON PUBLIC.FEDERATED_IDENTITY(IDENTITY_PROVIDER, USER_ID) VALUES ( /* key:28 */ 'lucy-test-github', '209cda49-89c5-4cf4-bdbb-aa976c155bb8')"; SQL statement:
keycloak-1  | insert into FEDERATED_IDENTITY (REALM_ID,TOKEN,FEDERATED_USER_ID,FEDERATED_USERNAME,IDENTITY_PROVIDER,USER_ID) values (?,?,?,?,?,?) [23505-230]], SQL: insert into FEDERATED_IDENTITY (REALM_ID,TOKEN,FEDERATED_USER_ID,FEDERATED_USERNAME,IDENTITY_PROVIDER,USER_ID) values (?,?,?,?,?,?)
keycloak-1  | 2024-11-18 18:44:59,609 WARN  [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (executor-thread-1) SQL Error: 23505, SQLState: 23505
keycloak-1  | 2024-11-18 18:44:59,609 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (executor-thread-1) Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_40 ON PUBLIC.FEDERATED_IDENTITY(IDENTITY_PROVIDER, USER_ID) VALUES ( /* key:28 */ 'lucy-test-github', '209cda49-89c5-4cf4-bdbb-aa976c155bb8')"; SQL statement:
keycloak-1  | insert into FEDERATED_IDENTITY (REALM_ID,TOKEN,FEDERATED_USER_ID,FEDERATED_USERNAME,IDENTITY_PROVIDER,USER_ID) values (?,?,?,?,?,?) [23505-230]
keycloak-1  | 2024-11-18 18:44:59,613 ERROR [org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (executor-thread-1) Failed to make identity provider oauth callback: org.keycloak.models.ModelDuplicateException: Duplicate resource error
keycloak-1  | 	at org.keycloak.connections.jpa.PersistenceExceptionConverter.convert(PersistenceExceptionConverter.java:103)
keycloak-1  | 	at org.keycloak.connections.jpa.PersistenceExceptionConverter.invoke(PersistenceExceptionConverter.java:68)
keycloak-1  | 	at jdk.proxy2/jdk.proxy2.$Proxy62.flush(Unknown Source)
keycloak-1  | 	at org.keycloak.models.jpa.JpaUserProvider.addFederatedIdentity(JpaUserProvider.java:175)
keycloak-1  | 	at org.keycloak.storage.UserStorageManager.addFederatedIdentity(UserStorageManager.java:632)
keycloak-1  | 	at org.keycloak.models.cache.infinispan.UserCacheSession.addFederatedIdentity(UserCacheSession.java:857)
keycloak-1  | 	at org.keycloak.services.resources.IdentityBrokerService.performAccountLinking(IdentityBrokerService.java:994)
keycloak-1  | 	at org.keycloak.services.resources.IdentityBrokerService.authenticated(IdentityBrokerService.java:572)
keycloak-1  | 	at org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint.authResponse(AbstractOAuth2IdentityProvider.java:568)
keycloak-1  | 	at org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider$Endpoint$quarkusrestinvoker$authResponse_ab908fbdd086ee82e140d8a818c077362a2d04b4.invoke(Unknown Source)
keycloak-1  | 	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
keycloak-1  | 	at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
keycloak-1  | 	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
keycloak-1  | 	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:635)
keycloak-1  | 	at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2516)
keycloak-1  | 	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2495)
keycloak-1  | 	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1521)
keycloak-1  | 	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
keycloak-1  | 	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
keycloak-1  | 	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
keycloak-1  | 	at java.base/java.lang.Thread.run(Thread.java:1583)
keycloak-1  | Caused by: org.h2.jdbc.JdbcBatchUpdateException: Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_40 ON PUBLIC.FEDERATED_IDENTITY(IDENTITY_PROVIDER, USER_ID) VALUES ( /* key:28 */ 'lucy-test-github', '209cda49-89c5-4cf4-bdbb-aa976c155bb8')"; SQL statement:
keycloak-1  | insert into FEDERATED_IDENTITY (REALM_ID,TOKEN,FEDERATED_USER_ID,FEDERATED_USERNAME,IDENTITY_PROVIDER,USER_ID) values (?,?,?,?,?,?) [23505-230]
keycloak-1  | 	at org.h2.jdbc.JdbcPreparedStatement.executeBatch(JdbcPreparedStatement.java:1277)
keycloak-1  | 	at io.agroal.pool.wrapper.StatementWrapper.executeBatch(StatementWrapper.java:340)
keycloak-1  | 	at org.hibernate.engine.jdbc.batch.internal.BatchImpl.lambda$performExecution$2(BatchImpl.java:279)
keycloak-1  | 	at org.hibernate.engine.jdbc.mutation.internal.PreparedStatementGroupSingleTable.forEachStatement(PreparedStatementGroupSingleTable.java:67)
keycloak-1  | 	at org.hibernate.engine.jdbc.batch.internal.BatchImpl.performExecution(BatchImpl.java:264)
keycloak-1  | 	at org.hibernate.engine.jdbc.batch.internal.BatchImpl.execute(BatchImpl.java:242)
keycloak-1  | 	at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:188)
keycloak-1  | 	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:674)
keycloak-1  | 	at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:511)
keycloak-1  | 	at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:414)
keycloak-1  | 	at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:41)
keycloak-1  | 	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127)
keycloak-1  | 	at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1429)
keycloak-1  | 	at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1415)
keycloak-1  | 	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
keycloak-1  | 	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
keycloak-1  | 	at org.keycloak.connections.jpa.PersistenceExceptionConverter.invoke(PersistenceExceptionConverter.java:66)
keycloak-1  | 	... 19 more
keycloak-1  |
keycloak-1  | 2024-11-18 18:44:59,615 WARN  [org.keycloak.events] (executor-thread-1) type="IDENTITY_PROVIDER_LOGIN_ERROR", realmId="0000175f-702e-4b99-9c99-c807a7158c1d", realmName="my-realm", clientId="my-client-id", userId="null", ipAddress="my-ip-address", error="identity_provider_login_failure", identity_provider="lucy-test-github", redirect_uri="http://example.com", identity_provider_identity="my-federated-username", code_id="e98b0c7d-7ad5-4145-ad25-6305010bd4c9"

Version

26.0.5

Regression

  • The issue is a regression

Expected behavior

The previously linked account is unlinked and replaced with the newly linked one.

Actual behavior

The above exception is thrown, and a generic "Unexpected error when authenticating with identity provider" page is shown to the client.

How to Reproduce?

  • Create an identity provider (I can reproduce with GitHub and a custom OAuth2 provider)
  • Link an account through client-initiated account linking
  • Attempt to link a second account the same way

Anything else?

I haven't tested this with a SAML client so this might be OAuth specific

贡献者指南