Skip to content

NullPointerException when GrantedAuthority.getAuthority() returns null #15243

Open
@Crystark

Description

@Crystark

Describe the bug
When my custom GrantedAuthority returns null on getAuthority(), I get the following exception that makes the application fail.

2024-06-13T17:44:59.000+02:00 ERROR 12657 --- [     parallel-2] a.w.r.e.AbstractErrorWebExceptionHandler : [cdb41c64-1]  500 Server Error for HTTP POST "/graphql"

java.lang.NullPointerException: The mapper [org.springframework.security.authorization.AuthorityReactiveAuthorizationManager$$Lambda/0x0000000800d06000] returned a null value.
	at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:115) ~[reactor-core-3.6.6.jar:3.6.6]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.FluxMapFuseable] :
	reactor.core.publisher.Flux.map(Flux.java:6580)
	org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.check(AuthorityReactiveAuthorizationManager.java:50)
Error has been observed at the following site(s):
	*______________Flux.map ⇢ at org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.check(AuthorityReactiveAuthorizationManager.java:50)
	|_             Flux.any ⇢ at org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.check(AuthorityReactiveAuthorizationManager.java:51)
	|_             Mono.map ⇢ at org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.check(AuthorityReactiveAuthorizationManager.java:52)
	|_  Mono.defaultIfEmpty ⇢ at org.springframework.security.authorization.AuthorityReactiveAuthorizationManager.check(AuthorityReactiveAuthorizationManager.java:53)
	*__________Mono.flatMap ⇢ at org.springframework.security.web.server.authorization.DelegatingReactiveAuthorizationManager.lambda$check$2(DelegatingReactiveAuthorizationManager.java:58)
	*________Flux.concatMap ⇢ at org.springframework.security.web.server.authorization.DelegatingReactiveAuthorizationManager.check(DelegatingReactiveAuthorizationManager.java:54)
	|_            Flux.next ⇢ at org.springframework.security.web.server.authorization.DelegatingReactiveAuthorizationManager.check(DelegatingReactiveAuthorizationManager.java:64)
	|_  Mono.defaultIfEmpty ⇢ at org.springframework.security.web.server.authorization.DelegatingReactiveAuthorizationManager.check(DelegatingReactiveAuthorizationManager.java:65)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.authorization.ObservationReactiveAuthorizationManager.lambda$check$4(ObservationReactiveAuthorizationManager.java:70)
	|_      Mono.doOnCancel ⇢ at org.springframework.security.authorization.ObservationReactiveAuthorizationManager.lambda$check$4(ObservationReactiveAuthorizationManager.java:76)
	|_       Mono.doOnError ⇢ at org.springframework.security.authorization.ObservationReactiveAuthorizationManager.lambda$check$4(ObservationReactiveAuthorizationManager.java:76)
	*__Mono.deferContextual ⇢ at org.springframework.security.authorization.ObservationReactiveAuthorizationManager.check(ObservationReactiveAuthorizationManager.java:66)
	|_          Mono.filter ⇢ at org.springframework.security.authorization.ReactiveAuthorizationManager.verify(ReactiveAuthorizationManager.java:52)
	|_   Mono.switchIfEmpty ⇢ at org.springframework.security.authorization.ReactiveAuthorizationManager.verify(ReactiveAuthorizationManager.java:53)
	|_         Mono.flatMap ⇢ at org.springframework.security.authorization.ReactiveAuthorizationManager.verify(ReactiveAuthorizationManager.java:54)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.authorization.AuthorizationWebFilter.filter(AuthorizationWebFilter.java:53)
	|_       Mono.doOnError ⇢ at org.springframework.security.web.server.authorization.AuthorizationWebFilter.filter(AuthorizationWebFilter.java:54)
	|_   Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authorization.AuthorizationWebFilter.filter(AuthorizationWebFilter.java:56)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_           checkpoint ⇢ AuthorizationWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	|_   Mono.onErrorResume ⇢ at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.filter(ExceptionTranslationWebFilter.java:53)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_           checkpoint ⇢ ExceptionTranslationWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	*__________Mono.flatMap ⇢ at org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter.filter(ServerRequestCacheWebFilter.java:41)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_           checkpoint ⇢ ServerRequestCacheWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_           checkpoint ⇢ SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	*_____________Mono.then ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.onAuthenticationSuccess(AuthenticationWebFilter.java:136)
	|_    Mono.contextWrite ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.onAuthenticationSuccess(AuthenticationWebFilter.java:137)
	*__________Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.authenticate(AuthenticationWebFilter.java:125)
	|_       Mono.doOnError ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.authenticate(AuthenticationWebFilter.java:127)
	*__________Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:115)
	|_   Mono.onErrorResume ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:116)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_           checkpoint ⇢ AuthenticationWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	|_    Mono.contextWrite ⇢ at org.springframework.security.web.server.context.ReactorContextWebFilter.filter(ReactorContextWebFilter.java:48)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_           checkpoint ⇢ ReactorContextWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_           checkpoint ⇢ HttpHeaderWriterWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	|_    Mono.contextWrite ⇢ at org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter.filter(ServerHttpSecurity.java:3968)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.wrapFilter(ObservationWebFilterChainDecorator.java:211)
	|_     Mono.doOnSuccess ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$AroundWebFilterObservation$SimpleAroundWebFilterObservation.lambda$wrap$6(ObservationWebFilterChainDecorator.java:367)
	|_      Mono.doOnCancel ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$AroundWebFilterObservation$SimpleAroundWebFilterObservation.lambda$wrap$6(ObservationWebFilterChainDecorator.java:368)
	|_       Mono.doOnError ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$AroundWebFilterObservation$SimpleAroundWebFilterObservation.lambda$wrap$6(ObservationWebFilterChainDecorator.java:369)
	|_    Mono.contextWrite ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$AroundWebFilterObservation$SimpleAroundWebFilterObservation.lambda$wrap$6(ObservationWebFilterChainDecorator.java:373)
	*__Mono.deferContextual ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilter.filter(ObservationWebFilterChainDecorator.java:193)
	|_           checkpoint ⇢ ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.security.web.server.ObservationWebFilterChainDecorator$ObservationWebFilterChain.filter(ObservationWebFilterChainDecorator.java:152)
	*__________Mono.flatMap ⇢ at org.springframework.security.web.server.WebFilterChainProxy.filter(WebFilterChainProxy.java:63)
	|_           checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
	|_       Mono.doFinally ⇢ at io.sentry.spring.jakarta.webflux.SentryWebFilter.filter(SentryWebFilter.java:39)
	|_       Mono.doOnError ⇢ at io.sentry.spring.jakarta.webflux.SentryWebFilter.filter(SentryWebFilter.java:40)
	|_         Mono.doFirst ⇢ at io.sentry.spring.jakarta.webflux.SentryWebFilter.filter(SentryWebFilter.java:41)
	|_           checkpoint ⇢ io.sentry.spring.jakarta.webflux.SentryWebFilter [DefaultWebFilterChain]
	*____________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:106)
	|_       Mono.doOnError ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:84)
	|_   Mono.onErrorResume ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:85)
	|_       Mono.doOnError ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:84)
	|_   Mono.onErrorResume ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:85)
	|_       Mono.doOnError ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:84)
	*____________Mono.error ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler$CheckpointInsertingHandler.handle(ExceptionHandlingWebHandler.java:106)
	|_           checkpoint ⇢ HTTP POST "/graphql" [ExceptionHandlingWebHandler]
	*__________Mono.flatMap ⇢ at io.sentry.spring.jakarta.webflux.SentryWebExceptionHandler.handle(SentryWebExceptionHandler.java:70)
Original Stack Trace:
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:115) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainAsync(FluxFlattenIterable.java:453) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:724) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onNext(FluxFlattenIterable.java:256) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2571) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:360) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.request(FluxFilterFuseable.java:411) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:360) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.request(FluxMapFuseable.java:360) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onSubscribe(FluxFlattenIterable.java:241) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onSubscribe(FluxFilterFuseable.java:305) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onSubscribe(FluxMapFuseable.java:265) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onNext(FluxPeekFuseable.java:503) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onNext(FluxFilterFuseable.java:118) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:492) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:424) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.request(FluxPeekFuseable.java:144) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:373) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onSubscribe(FluxPeekFuseable.java:178) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Mono.subscribe(Mono.java:4568) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onNext(FluxConcatMapNoPrefetch.java:207) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerComplete(FluxConcatMapNoPrefetch.java:275) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onComplete(FluxConcatMap.java:889) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:152) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:171) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onComplete(FluxPeekFuseable.java:595) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2573) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2367) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2241) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Mono.subscribe(Mono.java:4568) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:82) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:102) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:171) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:850) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:612) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:592) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFlatMap$FlatMapMain.request(FluxFlatMap.java:349) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2331) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.request(FluxPeekFuseable.java:437) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2331) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:339) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.request(FluxDefaultIfEmpty.java:98) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.request(FluxPeekFuseable.java:437) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.request(FluxFilterFuseable.java:191) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2367) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2241) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onSubscribe(FluxFilterFuseable.java:87) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onSubscribe(FluxPeekFuseable.java:471) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$BaseFluxToMonoOperator.onSubscribe(Operators.java:2051) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2097) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:134) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:152) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:152) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxFilterFuseable$FilterFuseableSubscriber.onComplete(FluxFilterFuseable.java:171) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:350) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1866) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.signalCached(MonoCacheTime.java:337) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoCacheTime$CoordinatorSubscriber.onNext(MonoCacheTime.java:354) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.publisher.MonoPublishOn$PublishOnSubscriber.run(MonoPublishOn.java:181) ~[reactor-core-3.6.6.jar:3.6.6]
		at io.sentry.spring.jakarta.webflux.SentryScheduleHook.lambda$apply$0(SentryScheduleHook.java:23) ~[sentry-spring-jakarta-7.9.0.jar:na]
		at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) ~[reactor-core-3.6.6.jar:3.6.6]
		at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) ~[reactor-core-3.6.6.jar:3.6.6]
		at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:317) ~[na:na]
		at java.base/java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:na]
		at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
		at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
		at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
		at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

To Reproduce
I can't provide a complete working example but hopefully the following samples will help:

First here is a condensed version of my base security config so that I can work with my permissions provider.
The key points are my asking of the access authority and that I can return null as a response to getAuthority which is allowed as per the documentation:

Returns: a representation of the granted authority (or null if the granted authority cannot be expressed as a String with sufficient precision).

@Configuration
class BaseSecurityConfig {
    @Bean
    fun reactiveJwtAuthenticationConverter() = ReactiveJwtAuthenticationConverter().apply {
        setJwtGrantedAuthoritiesConverter { jwt ->
            jwt.getClaim<Map<String, List<Map<String, Any>>>>("authorization")
                ?.get("permissions")
                ?.let { Flux.fromIterable(it) }
                ?.flatMap(::permissionToMyAuthority)
                ?: Flux.empty()
        }
    }

    @Bean
    fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain = http {
        csrf { disable() }
        formLogin { disable() }
        logout { disable() }
        httpBasic { disable() }
        authorizeExchange {
            authorize(anyExchange, hasAuthority("access"))
        }
        oauth2ResourceServer { jwt {} }
    }
}

fun permissionToMyAuthority(permission: Map<String, Any?>) = (permission["scopes"] as List<*>?)
    .takeUnless { it.isNullOrEmpty() }
    ?.let { scopes ->
        val rsname = permission["rsname"] as String?

        Flux.fromIterable(scopes)
            .cast(String::class.java) // We know we only have strings
            .map { MyAuthority(it, rsname) }
    } ?: Flux.empty()

data class MyAuthority(private val scope: String, private val resource: String?) : GrantedAuthority {

    override fun getAuthority(): String? = scope.takeIf { resource == null }

    fun hasPermission(scope: String, type: String, reference: String) =
        this.scope == scope && nameResource(type, reference) == resource
}

fun nameResource(type: String, reference: String) = "$type:$reference"

It seems that the order in which I process the permissions matter as if it finds the access authority it won't continue and I won't get the NPE. However if I have my nullable authority first it's going to fail.

Expected behavior
I expect org.springframework.security.authorization.AuthorityReactiveAuthorizationManager#check to not crash on a null authority and just filter it out.

Code proposition

	@Override
	public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
		// @formatter:off
		return authentication.filter(Authentication::isAuthenticated)
				.flatMapIterable(Authentication::getAuthorities)
				.mapNotNull(GrantedAuthority::getAuthority)
				.any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> authority.getAuthority().equals(grantedAuthority)))
				.map((granted) -> ((AuthorizationDecision) new AuthorityAuthorizationDecision(granted, this.authorities)))
				.defaultIfEmpty(new AuthorityAuthorizationDecision(false, this.authorities));
		// @formatter:on
	}

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: coreAn issue in spring-security-coretype: bugA general bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions