Expected Behavior
With the non-reactive SecurityContextHolder you can set the authentication from anywhere like this:
SecurityContextHolder.getContext().authentication = ..
But it seems like this is not possible for ReactiveSecurityContextHolder. The only way that I've found to work is from within a filter:
return chain.filter(exchange).contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))
Context
Doing this outside of a filter is required in a GraphQL application where additional downstream data loaders can need access to the authentication object. With the new Spring Graphql this example shows it:
@MutationMapping
suspend fun login(@Argument input: LoginInput): LoginResult {
// verify input and generate token
// not working
currentCoroutineContext()[ReactorContext]?.context
?.put(ReactiveSecurityContextHolder.withAuthentication(auth))
return LoginResult()
}
@SchemaMapping
suspend fun user(loginResult: LoginResult): User {
// This is always null
val auth = ReactiveSecurityContextHolder.getContext().awaitSingleOrNull()?.authentication
}
This is possible with a non-reactive SecurityContextHolder and Spring Web. This way of authenticating was blogged about here https://dimitr.im/graphql-spring-security
Comment From: sjohnr
Hi @eduanb! Thanks for reaching out.
With the non-reactive
SecurityContextHolderyou can set the authentication from anywhere like this:
While making the SecurityContext immutable would be a breaking change for users, I believe the intention is that it is not changed in the way you mention. The SecurityContextPersistenceFilter (now deprecated) had some issue, as I understand it, because the SecurityContext was not immutable. The recommended way to update the SecurityContext in a servlet application is:
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(...);
SecurityContextHolder.setContext(securityContext);
In 6.0, simply setting the authentication on an existing context would have no effect beyond the current thread as SecurityContextPersistenceFilter is no longer used by default.
Having said that, I don't believe it would actually be desired (let alone possible) to make the ReactiveSecurityContextHolder work as you suggest.
Doing this outside of a filter is required in a GraphQL application where additional downstream data loaders can need access to the authentication object.
I would recommend you ask if there is another way to accomplish what you're looking to do on stackoverflow. I lack knowledge of GraphQL but discussion of graphql-specific things would be out of scope for an issue here.
I'm going to close this issue for now, with the above explanation. If you feel I've misunderstood your request, please feel free to add clarifying comments or perhaps a minimal, reproducible sample if you still feel like a non-GraphQL specific example should work in Spring Security and we can re-open if needed.