Skip to content

✨ Keycloak - Map multiple IdP users to a single user via custom multi-value attribute#2598

Open
Copilot wants to merge 8 commits intomainfrom
copilot/map-multiple-idp-users
Open

✨ Keycloak - Map multiple IdP users to a single user via custom multi-value attribute#2598
Copilot wants to merge 8 commits intomainfrom
copilot/map-multiple-idp-users

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 27, 2026

  • Create new Maven module idp-linking-authenticator with pom.xml
  • Implement CustomAttributeIdpLinkingAuthenticator extending AbstractIdpAuthenticator
  • Implement CustomAttributeIdpLinkingAuthenticatorFactory with configurable parameters
  • Write 22 unit tests covering all scenarios (zero/one/multiple matches, all claim types, edge cases)
  • Update parent pom.xml to include new module
  • Update Dockerfile.keycloak to copy new JAR
  • Add Docusaurus documentation for the feature
  • Fix: shorten PROVIDER_ID from custom-attribute-idp-linking-authenticator (42 chars) to idp-linking-authenticator (25 chars) — Keycloak's AUTHENTICATION_EXECUTION.AUTHENTICATOR column is character varying(36)

Copilot AI and others added 2 commits April 27, 2026 16:08
…ti-IdP user linking

Agent-Logs-Url: https://github.com/sequentech/step/sessions/5cef1d48-5e8a-40c9-9642-9434ff744db5

Co-authored-by: xalsina-sequent <173030604+xalsina-sequent@users.noreply.github.com>
Agent-Logs-Url: https://github.com/sequentech/step/sessions/5cef1d48-5e8a-40c9-9642-9434ff744db5

Co-authored-by: xalsina-sequent <173030604+xalsina-sequent@users.noreply.github.com>
Copilot AI changed the title [WIP] Add feature to map multiple IdP users to a single Keycloak user feat(keycloak): Custom Attribute IdP Identity Linking authenticator for First Broker Login Apr 27, 2026
Copilot AI requested a review from xalsina-sequent April 27, 2026 16:12
@Findeton Findeton changed the title feat(keycloak): Custom Attribute IdP Identity Linking authenticator for First Broker Login ✨ Keycloak - Map multiple IdP users to a single user via custom multi-value attribute Apr 28, 2026
@github-actions
Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


Thank you for your contribution! Before we can merge this PR, we need you to sign our Contributor License Agreement (CLA).

To sign the CLA, please comment on this PR with:
I have read the CLA Document and I hereby sign the CLA

This is a one-time requirement. Once you have signed, all future contributions will be covered.

You can read the full CLA document here: https://github.com/sequentech/step/blob/main/.github/cla/CLA.md


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 29, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://docs.sequentech.io/docusaurus/pr-preview/pr-2598/

Built to branch doc-previews at 2026-05-04 14:29 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@xalsina-sequent
Copy link
Copy Markdown
Contributor

@copilot There's an error when adding the Authentication to a flow.

keycloak | 2026-04-29 11:11:17,463 INFO [org.hibernate.orm.jdbc.batch] (executor-thread-7) HHH100503: On release of batch it still contained JDBC statements
keycloak | 2026-04-29 11:11:17,467 WARN [org.hibernate.orm.jdbc.error] (executor-thread-7) HHH000247: ErrorCode: 0, SQLState: 22001
keycloak | 2026-04-29 11:11:17,468 WARN [org.hibernate.orm.jdbc.error] (executor-thread-7) Batch entry 0 insert into public.AUTHENTICATION_EXECUTION (AUTHENTICATOR,AUTH_CONFIG,AUTHENTICATOR_FLOW,AUTH_FLOW_ID,FLOW_ID,PRIORITY,REALM_ID,REQUIREMENT,ID) values (('custom-attribute-idp-linking-authenticator'),(NULL),('FALSE'::boolean),(NULL),('218c4223-bcb2-44d0-b3b4-0a6fa700f6d8'),('0'::int4),('5aa2e3b3-e0f2-4bee-a80f-08556826970b'),('3'::int2),('9ec60049-f5b5-4c13-8f3b-be54510094a9')) was aborted: ERROR: value too long for type character varying(36) Call getNextException to see other errors in the batch.
keycloak | 2026-04-29 11:11:17,468 WARN [org.hibernate.orm.jdbc.error] (executor-thread-7) HHH000247: ErrorCode: 0, SQLState: 22001
keycloak | 2026-04-29 11:11:17,468 WARN [org.hibernate.orm.jdbc.error] (executor-thread-7) Batch entry 0 insert into public.AUTHENTICATION_EXECUTION (AUTHENTICATOR,AUTH_CONFIG,AUTHENTICATOR_FLOW,AUTH_FLOW_ID,FLOW_ID,PRIORITY,REALM_ID,REQUIREMENT,ID) values (('custom-attribute-idp-linking-authenticator'),(NULL),('FALSE'::boolean),(NULL),('218c4223-bcb2-44d0-b3b4-0a6fa700f6d8'),('0'::int4),('5aa2e3b3-e0f2-4bee-a80f-08556826970b'),('3'::int2),('9ec60049-f5b5-4c13-8f3b-be54510094a9')) was aborted: ERROR: value too long for type character varying(36) Call getNextException to see other errors in the batch.
keycloak | 2026-04-29 11:11:17,485 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-7) Uncaught server error: org.keycloak.models.ModelException: Database operation failed
keycloak | at org.keycloak.connections.jpa.support.EntityManagerProxy.convert(EntityManagerProxy.java:150)
keycloak | at org.keycloak.connections.jpa.PersistenceExceptionConverter.convert(PersistenceExceptionConverter.java:30)
keycloak | at org.keycloak.connections.jpa.JpaExceptionConverter.convert(JpaExceptionConverter.java:31)
keycloak | at org.keycloak.transaction.JtaTransactionWrapper.lambda$handleException$0(JtaTransactionWrapper.java:77)
keycloak | at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
keycloak | at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1787)
keycloak | at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
keycloak | at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
keycloak | at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
keycloak | at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
keycloak | at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
keycloak | at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
keycloak | at org.keycloak.transaction.JtaTransactionWrapper.handleException(JtaTransactionWrapper.java:79)
keycloak | at org.keycloak.transaction.JtaTransactionWrapper.commit(JtaTransactionWrapper.java:104)
keycloak | at org.keycloak.services.DefaultKeycloakTransactionManager.lambda$commitWithTracing$1(DefaultKeycloakTransactionManager.java:170)
keycloak | at org.keycloak.tracing.NoopTracingProvider.trace(NoopTracingProvider.java:59)
keycloak | at org.keycloak.tracing.NoopTracingProvider.trace(NoopTracingProvider.java:69)
keycloak | at org.keycloak.services.DefaultKeycloakTransactionManager.commitWithTracing(DefaultKeycloakTransactionManager.java:169)
keycloak | at org.keycloak.services.DefaultKeycloakTransactionManager.lambda$commit$0(DefaultKeycloakTransactionManager.java:136)
keycloak | at org.keycloak.tracing.NoopTracingProvider.trace(NoopTracingProvider.java:59)
keycloak | at org.keycloak.tracing.NoopTracingProvider.trace(NoopTracingProvider.java:69)
keycloak | at org.keycloak.services.DefaultKeycloakTransactionManager.commit(DefaultKeycloakTransactionManager.java:121)
keycloak | at org.keycloak.services.DefaultKeycloakSession.closeTransactionManager(DefaultKeycloakSession.java:392)
keycloak | at org.keycloak.services.DefaultKeycloakSession.close(DefaultKeycloakSession.java:357)
keycloak | at org.keycloak.models.KeycloakBeanProducer_ProducerMethod_getKeycloakSession_XoSEUTXOsE3bpqXlGMAykCiECUM_ClientProxy.close(Unknown Source)
keycloak | at org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler.close(TransactionalSessionHandler.java:60)
keycloak | at org.keycloak.quarkus.runtime.integration.jaxrs.CloseSessionFilter.closeSession(CloseSessionFilter.java:67)
keycloak | at org.keycloak.quarkus.runtime.integration.jaxrs.CloseSessionFilter.filter(CloseSessionFilter.java:63)
keycloak | at org.jboss.resteasy.reactive.server.handlers.ResourceResponseFilterHandler.handle(ResourceResponseFilterHandler.java:25)
keycloak | at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:192)
keycloak | at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
keycloak | at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:645)
keycloak | at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2651)
keycloak | at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2630)
keycloak | at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1622)
keycloak | at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1589)
keycloak | at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
keycloak | at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
keycloak | at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
keycloak | at java.base/java.lang.Thread.run(Thread.java:1583)
keycloak | Caused by: java.sql.BatchUpdateException: Batch entry 0 insert into public.AUTHENTICATION_EXECUTION (AUTHENTICATOR,AUTH_CONFIG,AUTHENTICATOR_FLOW,AUTH_FLOW_ID,FLOW_ID,PRIORITY,REALM_ID,REQUIREMENT,ID) values (('custom-attribute-idp-linking-authenticator'),(NULL),('FALSE'::boolean),(NULL),('218c4223-bcb2-44d0-b3b4-0a6fa700f6d8'),('0'::int4),('5aa2e3b3-e0f2-4bee-a80f-08556826970b'),('3'::int2),('9ec60049-f5b5-4c13-8f3b-be54510094a9')) was aborted: ERROR: value too long for type character varying(36) Call getNextException to see other errors in the batch.
keycloak | at org.postgresql.jdbc.BatchResultHandler.handleError(BatchResultHandler.java:165)
keycloak | at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2422)
keycloak | at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:580)
keycloak | at org.postgresql.jdbc.PgStatement.internalExecuteBatch(PgStatement.java:886)
keycloak | at org.postgresql.jdbc.PgStatement.executeBatch(PgStatement.java:910)
keycloak | at org.postgresql.jdbc.PgPreparedStatement.executeBatch(PgPreparedStatement.java:1778)
keycloak | at io.agroal.pool.wrapper.StatementWrapper.executeBatch(StatementWrapper.java:342)
keycloak | at org.hibernate.engine.jdbc.batch.internal.BatchImpl.lambda$performExecution$1(BatchImpl.java:264)
keycloak | at org.hibernate.engine.jdbc.mutation.internal.PreparedStatementGroupSingleTable.forEachStatement(PreparedStatementGroupSingleTable.java:63)
keycloak | at org.hibernate.engine.jdbc.batch.internal.BatchImpl.performExecution(BatchImpl.java:253)
keycloak | at org.hibernate.engine.jdbc.batch.internal.BatchImpl.execute(BatchImpl.java:232)
keycloak | at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:191)
keycloak | at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:676)
keycloak | at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:513)
keycloak | at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:378)
keycloak | at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
keycloak | at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:140)
keycloak | at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1447)
keycloak | at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:488)
keycloak | at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2325)
keycloak | at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2033)
keycloak | at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:394)
keycloak | at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.beforeCompletion(JtaTransactionCoordinatorImpl.java:328)
keycloak | at org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:39)
keycloak | at org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:33)
keycloak | at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:52)
keycloak | at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:348)
keycloak | at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:66)
keycloak | at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:135)
keycloak | at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1307)
keycloak | at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:104)
keycloak | at io.quarkus.narayana.jta.runtime.NotifyingTransactionManager.commit(NotifyingTransactionManager.java:70)
keycloak | at org.keycloak.transaction.JtaTransactionWrapper.commit(JtaTransactionWrapper.java:102)
keycloak | ... 26 more
keycloak | Caused by: org.postgresql.util.PSQLException: ERROR: value too long for type character varying(36)
keycloak | at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2734)
keycloak | at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2421)
keycloak | ... 57 more

Agent-Logs-Url: https://github.com/sequentech/step/sessions/2f7aa4a3-f3f4-48ad-bac0-8283baa2f90c

Co-authored-by: xalsina-sequent <173030604+xalsina-sequent@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 29, 2026

@copilot There's an error when adding the Authentication to a flow.

keycloak | 2026-04-29 11:11:17,463 INFO [org.hibernate.orm.jdbc.batch] (executor-thread-7) HHH100503: On release of batch it stil...

Fixed in c8e05d8. The PROVIDER_ID custom-attribute-idp-linking-authenticator was 42 characters, exceeding the 36-character varchar(36) limit on Keycloak's AUTHENTICATION_EXECUTION.AUTHENTICATOR column. Shortened to idp-linking-authenticator (25 chars).

@xalsina-sequent xalsina-sequent marked this pull request as ready for review May 4, 2026 15:37
Copilot AI review requested due to automatic review settings May 4, 2026 15:38
@Findeton Findeton requested review from Copilot and removed request for Copilot May 6, 2026 23:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants