Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subtypes of Authentication are not injected as expected #1430

Open
Spikhalskiy opened this issue Aug 30, 2023 · 4 comments · Fixed by #1524
Open

Subtypes of Authentication are not injected as expected #1430

Spikhalskiy opened this issue Aug 30, 2023 · 4 comments · Fixed by #1524

Comments

@Spikhalskiy
Copy link

Spikhalskiy commented Aug 30, 2023

Expected Behavior

The following works correctly and method gets access to an instance of our own class TokenAuthentication

data class TokenAuthentication(
    private val token: Token,
) : io.micronaut.security.authentication.Authentication {
}

@Secured(SecurityRule.IS_AUTHENTICATED)
@Controller("/some")
class SomeController {
    @Post("/{var1}/method")
    fun method(
            @PathVariable var1: String,
            @Body request: SomeOurRequest,
            userToken: io.micronaut.security.authentication.Authentication,
        ): HttpResponse<Unit>
}

But it requires an ugly cast in every method. We expect that

@Post("/{var1}/method")
fun method(
        @PathVariable var1: String,
        @Body request: SomeOurRequest,
        userToken: our.package.TokenAuthentication,
    ): HttpResponse<Unit>

will also work and save us from Authentication -> TokenAuthentication cast.

Actual Behaviour

It looks like that if a custom subclass of io.micronaut.security.authentication.Authentication is used, Micronaut doesn't pick it up as a Authentication and tries to deserialize body of the request into this object the second time.

17:21:39.605 [default-nioEventLoopGroup-4-4] ERROR i.m.http.server.RouteExecutor - Unexpected error occurred: Already claimed
java.lang.IllegalStateException: Already claimed
	at io.micronaut.http.server.netty.body.ManagedBody.checkUnclaimed(ManagedBody.java:76)
	at io.micronaut.http.server.netty.body.ManagedBody.prepareClaim(ManagedBody.java:70)
	at io.micronaut.http.server.netty.body.ImmediateByteBody.processSingle(ImmediateByteBody.java:124)
	at io.micronaut.http.server.netty.binders.NettyBodyAnnotationBinder.transform(NettyBodyAnnotationBinder.java:145)
	at io.micronaut.http.server.netty.binders.NettyBodyAnnotationBinder$1.lambda$new$0(NettyBodyAnnotationBinder.java:101)
	at io.micronaut.core.execution.ImperativeExecutionFlowImpl.flatMap(ImperativeExecutionFlowImpl.java:72)
	at io.micronaut.http.server.netty.binders.NettyBodyAnnotationBinder$1.<init>(NettyBodyAnnotationBinder.java:99)
	at io.micronaut.http.server.netty.binders.NettyBodyAnnotationBinder.bindFullBody(NettyBodyAnnotationBinder.java:92)
	at io.micronaut.http.server.netty.binders.NettyBodyAnnotationBinder.bindFullBodyConvertibleValues(NettyBodyAnnotationBinder.java:74)
	at io.micronaut.http.bind.binders.DefaultBodyAnnotationBinder.bindBodyPart(DefaultBodyAnnotationBinder.java:84)
	at io.micronaut.http.server.netty.binders.NettyBodyAnnotationBinder.bindBodyPart(NettyBodyAnnotationBinder.java:60)
	at io.micronaut.http.bind.binders.DefaultBodyAnnotationBinder.bind(DefaultBodyAnnotationBinder.java:68)
	at io.micronaut.http.bind.binders.DefaultBodyAnnotationBinder.bind(DefaultBodyAnnotationBinder.java:35)
	at io.micronaut.http.bind.binders.DefaultUnmatchedRequestArgumentBinder.bind(DefaultUnmatchedRequestArgumentBinder.java:71)
	at io.micronaut.http.bind.binders.DefaultUnmatchedRequestArgumentBinder.bind(DefaultUnmatchedRequestArgumentBinder.java:35)
	at io.micronaut.web.router.AbstractRouteMatch.fulfillValue(AbstractRouteMatch.java:358)
	at io.micronaut.web.router.AbstractRouteMatch.fulfillBeforeFilters(AbstractRouteMatch.java:314)
	at io.micronaut.http.server.binding.RequestArgumentSatisfier.fulfillArgumentRequirementsBeforeFilters(RequestArgumentSatisfier.java:57)
	at io.micronaut.http.server.netty.NettyRequestArgumentSatisfier.fulfillArgumentRequirementsBeforeFilters(NettyRequestArgumentSatisfier.java:50)
	at io.micronaut.http.server.RequestLifecycle.fulfillArguments(RequestLifecycle.java:417)
	at io.micronaut.http.server.netty.NettyRequestLifecycle.fulfillArguments(NettyRequestLifecycle.java:121)
	at io.micronaut.http.server.RequestLifecycle.lambda$normalFlow$4(RequestLifecycle.java:145)
	at io.micronaut.http.server.RequestLifecycle.lambda$runWithFilters$14(RequestLifecycle.java:264)
	at io.micronaut.http.filter.FilterRunner.processRequestFilter(FilterRunner.java:306)
	at io.micronaut.http.filter.FilterRunner.filterRequest0(FilterRunner.java:183)
	at io.micronaut.http.filter.FilterRunner.lambda$filterRequest0$3(FilterRunner.java:183)
	at io.micronaut.http.filter.FilterRunner$FilterChainImpl.proceed(FilterRunner.java:970)
	at io.micronaut.security.filters.SecurityFilter.lambda$checkRules$7(SecurityFilter.java:164)
	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:163)
	at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
	at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:258)
	at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:863)
	at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
	at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545)
	at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.request(FluxFilterFuseable.java:411)
	at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
	at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152)
	at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onSubscribe(FluxFilterFuseable.java:305)
	at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55)
	at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
	at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onNext(FluxConcatMapNoPrefetch.java:206)
	at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335)
	at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
	at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerComplete(FluxConcatMapNoPrefetch.java:274)
	at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onComplete(FluxConcatMap.java:887)
	at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:299)
	at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:391)
	at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2547)
	at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.request(FluxFilterFuseable.java:411)
	at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2305)
	at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:338)
	at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108)
	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onSubscribe(MonoFlatMapMany.java:141)
	at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70)
	at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164)
	at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
	at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
	at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:81)
	at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
	at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmit(FluxFlatMap.java:544)
	at reactor.core.publisher.FluxFlatMap$FlatMapInner.onNext(FluxFlatMap.java:985)
	at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122)
	at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:489)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:422)
	at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335)
	at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371)
	at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
	at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
	at reactor.core.publisher.Mono.subscribe(Mono.java:4495)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:427)
	at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:335)
	at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:294)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:371)
	at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
	at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
	at io.micronaut.core.async.propagation.ReactivePropagation$1.subscribe(ReactivePropagation.java:54)
	at io.micronaut.core.async.propagation.ReactivePropagation$1.subscribe(ReactivePropagation.java:61)
	at reactor.core.publisher.MonoFromPublisher.subscribe(MonoFromPublisher.java:67)
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
	at io.micronaut.core.async.propagation.ReactivePropagation$1.subscribe(ReactivePropagation.java:54)
	at io.micronaut.core.async.propagation.ReactivePropagation$1.subscribe(ReactivePropagation.java:61)
	at reactor.core.publisher.MonoFromPublisher.subscribe(MonoFromPublisher.java:67)
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
	at io.micronaut.http.reactive.execution.ReactorExecutionFlowImpl.onComplete(ReactorExecutionFlowImpl.java:89)
	at io.micronaut.http.server.netty.NettyRequestLifecycle.handleNormal(NettyRequestLifecycle.java:87)
	at io.micronaut.http.server.netty.RoutingInBoundHandler.accept(RoutingInBoundHandler.java:220)
	at io.micronaut.http.server.netty.websocket.NettyServerWebSocketUpgradeHandler.accept(NettyServerWebSocketUpgradeHandler.java:156)
	at io.micronaut.http.server.netty.handler.PipeliningServerHandler$OptimisticBufferingInboundHandler.read(PipeliningServerHandler.java:422)
	at io.micronaut.http.server.netty.handler.PipeliningServerHandler.channelRead(PipeliningServerHandler.java:206)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:88)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)
	at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93)
	at io.netty.handler.codec.http.HttpServerKeepAliveHandler.channelRead(HttpServerKeepAliveHandler.java:64)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.micronaut.http.server.netty.handler.accesslog.HttpAccessLogHandler.channelRead(HttpAccessLogHandler.java:143)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
	at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)

Version

platform: 4.0.5

@Spikhalskiy Spikhalskiy changed the title Subtypes of Authentication are not injected Subtypes of Authentication are not injected as expected Aug 30, 2023
@Spikhalskiy
Copy link
Author

Spikhalskiy commented Aug 30, 2023

I was able to solve it by registering

@Singleton
class TokenAuthenticationArgumentBinder : AbstractPrincipalArgumentBinder< TokenAuthentication >(
    TokenAuthentication::class.java,
)

but it feels like it shouldn't have to be done explicitly.

@Spikhalskiy
Copy link
Author

If such a binder is really expected to be present by design, it would be great if the error message could be improved to point out on the fact that there is no applicable RequestArgumentBinder found for the class.

@sdelamo
Copy link
Contributor

sdelamo commented Sep 1, 2023

Yes, I think the creation of the RequestArgumentBinder is required. we should improve the docs.

@Spikhalskiy
Copy link
Author

Also, it would be great if an error message could be replaced with something meaningful. If you know netty, it's clear what happens here - we try to use the body a second time. But for a lot of users, it may be a very obscure exception.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: Done
3 participants