feature: Integration with WebFlux ReactiveSecurityContextHolder

See original GitHub issue

Describe the Feature Request

Currently we are trying to access ReactiveSecurityContextHolder to fetch the Principal, since we are dependant on Roles to show the proper Data.

e.g.:

  @DgsQuery(field = "stuffByFilter")
  public CompletionStage<List<Stuff>> leaves(@InputArgument("filter") final StuffFilter filter) {
    return ReactiveSecurityContextHolder.getContext()
      .map(SecurityContext::getAuthentication)
      .map(Authentication::getPrincipal)
      .map(Object::toString)
      .doOnNext(log::info)
      .flatMapMany(principal -> randomStuff())
      .collectList()
      .toFuture();
  }

The above code returns empty list as no logging is generated and no breakpoints are hit.

Describe Preferred Solution

Ideally we could get the current Principal available in the WebFlux Call chain via ReactiveSecurityContextHolder

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:14 (8 by maintainers)

github_iconTop GitHub Comments

4reactions
AlexRiedlercommented, Jun 19, 2021

Okay, after messing around inside dgs-framework, I think I have a path forward but would love input from the maintainers.

I modified DataFetcherResultProcessor:

 interface DataFetcherResultProcessor {
     fun supportsType(originalResult: Any): Boolean
     fun process(originalResult: Any, dfe: DataFetchingEnvironment): Any
 }

this is a relatively trivial change, allowing access to the dfe (or potential we should allow access to DgsDataFetchingEnvironment instead) inside of Mono/FluxDataFetcherResultProcessors.

I then had to implement my own custom Mono/Flux implementation converters since spring-security is not included in these modules.

class MonoDataFetcherResultProcessor : DataFetcherResultProcessor {
    override fun supportsType(originalResult: Any): Boolean {
        return originalResult is Mono<*>
    }

    override fun process(originalResult: Any, dfe: DataFetchingEnvironment): Any {
        if (originalResult is Mono<*>) {
            val context = ReactiveSecurityContextHolder.withAuthentication((dfe.getContext<DgsContext>().customContext as SecurityContext).authentication)
            return originalResult.contextWrite(context).toFuture()
        } else {
            throw IllegalArgumentException("Instance passed to ${this::class.qualifiedName} was not a Mono<*>. It was a ${originalResult::class.qualifiedName} instead")
        }
    }
}

noting that my customContext is:

@Component
class SecurityContextDgs : DgsReactiveCustomContextBuilderWithRequest<SecurityContext> {
    override fun build(
        extensions: Map<String, Any>?,
        headers: HttpHeaders?,
        serverRequest: ServerRequest?
    ): Mono<SecurityContext> {
        return ReactiveSecurityContextHolder.getContext()
    }
}

This could probably be improved in some way, but it does work e2e for me on my fork + custom implementations.

What are peoples thoughts and suggestions?

1reaction
denniseffingcommented, May 12, 2022

@gnoeley Yes! We discovered that the existing solution proposed in https://github.com/Netflix/dgs-framework/issues/375#issuecomment-864326805 has another issue though: It works for @DgsQuery but does not work for @DgsEntityFetcher. Would it be possible to consider this in your current PR so that it works for @DgsEntityFetcher as well?

Read more comments on GitHub >

github_iconTop Results From Across the Web

ReactiveSecurityContextHolder is empty in Spring WebFlux
I am trying to use the ReactiveSecurityContextHolder with Spring WebFlux. Unfortunately, the SecurityContext is empty :
Read more >
EnableReactiveMethodSecurity :: Spring Security
When integrating with WebFlux Security, the Reactor Context is automatically established by Spring Security according to the authenticated user: Java. Kotlin. @ ...
Read more >
Reactive Spring Security 5 Hands-On Workshop
Implement automated security integration tests ... Spring Webflux depends on Reactor and uses it internally to compose asynchronous logic ...
Read more >
JWT Authentication in Spring Boot Webflux | by Jaiden Ashmore
If the Authentication object can be authenticated, it will be added to the ReactiveSecurityContextHolder for usage by the subsequent ...
Read more >
Introduction to Spring WebFlux - /dev/solita
And then Java 8 introduced lambda expressions as a new language feature bringing functional style programming to Java.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found