Skip to content

Add DelegatingSecurityContextThreadFactory#19220

Open
klouds27 wants to merge 1 commit into
spring-projects:mainfrom
klouds27:fix/gh-19075-delegating-security-context-thread-factory
Open

Add DelegatingSecurityContextThreadFactory#19220
klouds27 wants to merge 1 commit into
spring-projects:mainfrom
klouds27:fix/gh-19075-delegating-security-context-thread-factory

Conversation

@klouds27
Copy link
Copy Markdown

Problem

There is no ThreadFactory equivalent in the DelegatingSecurityContext* family, making it impossible to propagate the SecurityContext to libraries that accept a ThreadFactory rather than an Executor. The primary motivation is structured concurrency: the proposed StructuredTaskScope API (JEP 533) accepts a ThreadFactory to create subtask threads, so without this class there is no supported way to propagate the security context to those threads.

Fix

Adds DelegatingSecurityContextThreadFactory, which wraps each Runnable passed to newThread() in a DelegatingSecurityContextRunnable before delegating to the underlying ThreadFactory. It follows the same design as DelegatingSecurityContextExecutor:

  • Two constructors: explicit SecurityContext or capture-at-call-time (null)
  • setSecurityContextHolderStrategy for custom holder strategies
  • Extends AbstractDelegatingSecurityContextSupport to reuse wrapping logic

Tests

  • newThreadWhenPlatformThreadThenSecurityContextPropagated — context propagated via platform thread
  • newThreadWhenVirtualThreadThenSecurityContextPropagated — context propagated via virtual thread (disabled on JRE 17)
  • newThreadWhenExplicitSecurityContextThenUsesThatContext — explicit context is used
  • newThreadWhenCurrentContextThenPropagatesCallerContext — null constructor captures caller context
  • constructorWhenNullDelegateThenException — null delegate rejected

Closes gh-19075

Adds DelegatingSecurityContextThreadFactory to propagate the
SecurityContext to threads created via ThreadFactory, following
the same pattern as DelegatingSecurityContextExecutor.

This enables SecurityContext propagation for APIs that accept a
ThreadFactory directly, such as structured concurrency (JEP 533).

Closes spring-projectsgh-19075

Signed-off-by: klouds27 <adalwolf@gmail.com>
Copilot AI review requested due to automatic review settings May 25, 2026 23:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new ThreadFactory implementation that propagates Spring Security SecurityContext into created threads, along with unit tests to validate behavior for platform threads, virtual threads, and explicit/current contexts.

Changes:

  • Introduce DelegatingSecurityContextThreadFactory that wraps Runnables with security-context propagation.
  • Add JUnit tests covering constructor validation and context propagation scenarios (platform/virtual threads, explicit/current contexts).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextThreadFactory.java Adds a delegating ThreadFactory that wraps tasks with DelegatingSecurityContextRunnable via existing support utilities.
core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextThreadFactoryTests.java Adds coverage for propagation behavior and constructor validation, including a virtual-thread scenario.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +95 to +105
private SecurityContext runAndCapture(DelegatingSecurityContextThreadFactory factory) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<SecurityContext> result = new AtomicReference<>();
Thread thread = factory.newThread(() -> {
result.set(SecurityContextHolder.getContext());
latch.countDown();
});
thread.start();
latch.await();
return result.get();
}
Comment on lines +59 to +64
@Test
@DisabledOnJre(JRE.JAVA_17)
public void newThreadWhenVirtualThreadThenSecurityContextPropagated() throws Exception {
SecurityContext context = propagateAndCapture(new VirtualThreadTaskExecutor().getVirtualThreadFactory());
assertThat(context.getAuthentication()).isNotNull();
}
}

@Override
public Thread newThread(Runnable r) {
Comment on lines +56 to +63
assertThat(context.getAuthentication()).isNotNull();
}

@Test
@DisabledOnJre(JRE.JAVA_17)
public void newThreadWhenVirtualThreadThenSecurityContextPropagated() throws Exception {
SecurityContext context = propagateAndCapture(new VirtualThreadTaskExecutor().getVirtualThreadFactory());
assertThat(context.getAuthentication()).isNotNull();
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add DelegatingSecurityContextThreadFactory

3 participants