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

ChainAuthHandlerImpl#postAuthentication throws IndexOutOfBoundsException #2641

Closed
andrei-tulba opened this issue Sep 1, 2024 · 4 comments
Closed
Assignees
Labels
Milestone

Comments

@andrei-tulba
Copy link

Version

4.5.9

Context

This commit f856512 causes an IndexOutOfBoundsException when calling the postAuthentication method in nested chain auth hadlers.

Do you have a reproducer?

ChainAuthHandlerImplTest

Tip

Cast an eye on README for pre-requisites and installation guide.

Steps to reproduce

./mvnw -Dtest=org.vaaron.vertx.web.handler.ChainAuthHandlerImplTest test

Extra

Linked issues

Stacktrace

java.lang.IndexOutOfBoundsException: Index 2 out of bounds for length 2
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
	at java.base/java.util.Objects.checkIndex(Objects.java:385)
	at java.base/java.util.ArrayList.get(ArrayList.java:427)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.postAuthentication(ChainAuthHandlerImpl.java:144)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.postAuthentication(ChainAuthHandlerImpl.java:144)
	at io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl.lambda$handle$0(AuthenticationHandlerImpl.java:98)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.lambda$iterate$0(ChainAuthHandlerImpl.java:116)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.lambda$iterate$0(ChainAuthHandlerImpl.java:116)
	at io.vertx.ext.web.handler.impl.SimpleAuthenticationHandlerImpl.lambda$authenticate$1(SimpleAuthenticationHandlerImpl.java:32)
	at io.vertx.core.impl.future.SucceededFuture.onSuccess(SucceededFuture.java:64)
	at io.vertx.ext.web.handler.impl.SimpleAuthenticationHandlerImpl.authenticate(SimpleAuthenticationHandlerImpl.java:32)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.iterate(ChainAuthHandlerImpl.java:83)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.authenticate(ChainAuthHandlerImpl.java:59)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.iterate(ChainAuthHandlerImpl.java:83)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.lambda$iterate$0(ChainAuthHandlerImpl.java:99)
	at io.vertx.ext.web.handler.impl.SimpleAuthenticationHandlerImpl.lambda$authenticate$0(SimpleAuthenticationHandlerImpl.java:29)
	at io.vertx.core.impl.future.FailedFuture.onFailure(FailedFuture.java:91)
	at io.vertx.ext.web.handler.impl.SimpleAuthenticationHandlerImpl.authenticate(SimpleAuthenticationHandlerImpl.java:25)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.iterate(ChainAuthHandlerImpl.java:83)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.lambda$iterate$0(ChainAuthHandlerImpl.java:99)
	at io.vertx.ext.web.handler.impl.SimpleAuthenticationHandlerImpl.lambda$authenticate$0(SimpleAuthenticationHandlerImpl.java:29)
	at io.vertx.core.impl.future.FailedFuture.onFailure(FailedFuture.java:91)
	at io.vertx.ext.web.handler.impl.SimpleAuthenticationHandlerImpl.authenticate(SimpleAuthenticationHandlerImpl.java:25)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.iterate(ChainAuthHandlerImpl.java:83)
	at io.vertx.ext.web.handler.impl.ChainAuthHandlerImpl.authenticate(ChainAuthHandlerImpl.java:59)
	at io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl.handle(AuthenticationHandlerImpl.java:84)
	at io.vertx.ext.web.handler.impl.AuthenticationHandlerImpl.handle(AuthenticationHandlerImpl.java:31)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:140)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
	at io.vertx.ext.web.handler.impl.BodyHandlerImpl.handle(BodyHandlerImpl.java:95)
	at io.vertx.ext.web.handler.impl.BodyHandlerImpl.handle(BodyHandlerImpl.java:45)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1285)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:68)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:67)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:30)
	at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:328)
	at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:164)
	at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:174)
	at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:159)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153)
	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.websocketx.extensions.WebSocketServerExtensionHandler.onHttpRequestChannelRead(WebSocketServerExtensionHandler.java:158)
	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:82)
	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.vertx.core.http.impl.Http1xUpgradeToH2CHandler.channelRead(Http1xUpgradeToH2CHandler.java:124)
	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.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
	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.vertx.core.http.impl.Http1xOrH2CHandler.end(Http1xOrH2CHandler.java:61)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.channelRead(Http1xOrH2CHandler.java:38)
	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.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1407)
	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:918)
	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:994)
	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:1583)
@tsegismont
Copy link
Contributor

Thanks for the reproducer. Can you elaborate about your use case in production? I understand it is possible to create a configuration that fails as such, but I'm not sure yet why nested chains are needed.

@andrei-tulba
Copy link
Author

  1. JWT Types: There are three types of JSON Web Tokens (JWTs) used in our production code. Each type is processed through a series of handlers.

  2. 3rd Type of JWT: The third type of JWT includes user claims. Sometimes, there's an additional, optional token that represents a more privileged user. This additional token is sent as a custom header and is managed by a separate handler. Here we wrap into an all chain handler these 2 handlers

  3. Integration Testing: For integration testing, we use a static token of the third type JWT. In this context chain handler from bullet 2 get wrapped into another any chain handler in addition with a simple authentication handler (checks a static test JWT).

Production schema:

  • ExternalM2MJWTHandler (M2M -> Machine-to-Machine)
  • CloudM2MJwtHandler
  • AllChainAuthHandler
    • IAMHandler (IAM -> Identity Access Management)
    • SuperUserIAMHandler (handles more privileged user token or ACKs if no such)

Integration tests:

  • ExternalM2MJWTHandler (M2M -> Machine-to-Machine)
  • CloudM2MJwtHandler
  • AnyChainAuthHandler
    • TestIAMHandler (Handles static test token)
    • AllChainAuthHandler
      • IAMHandler (IAM -> Identity Access Management)
      • SuperUserIAMHandler

@tsegismont
Copy link
Contributor

Thanks for the details @andrei-tulba

@tsegismont tsegismont added this to the 4.5.11 milestone Sep 5, 2024
@tsegismont tsegismont self-assigned this Sep 5, 2024
tsegismont added a commit to tsegismont/vertx-web that referenced this issue Sep 5, 2024
See vert-x3#2641

ChainAuthHandlerImpl maintains, for instances of type "any", an index indicating which handler in the list succeeded to authenticate the user.

This index is used in the postAuthentication method to determine which handler int he list should have its postAuthentication method invoked.

When "any" ChainAuthHandlers were nested, the index computed in the top level ChainAuthHandler was used to choose the handlers in the list of nested ChainAuthHandler.

This caused either the wrong handler to be peeked or IndexOutOfBoundsException to be thrown.

With this change, the value put in the RoutingContext is specific to a ChainAuthHandler instance.

Signed-off-by: Thomas Segismont <[email protected]>
tsegismont added a commit that referenced this issue Sep 5, 2024
See #2641

ChainAuthHandlerImpl maintains, for instances of type "any", an index indicating which handler in the list succeeded to authenticate the user.

This index is used in the postAuthentication method to determine which handler int he list should have its postAuthentication method invoked.

When "any" ChainAuthHandlers were nested, the index computed in the top level ChainAuthHandler was used to choose the handlers in the list of nested ChainAuthHandler.

This caused either the wrong handler to be peeked or IndexOutOfBoundsException to be thrown.

With this change, the value put in the RoutingContext is specific to a ChainAuthHandler instance.

Signed-off-by: Thomas Segismont <[email protected]>
tsegismont added a commit that referenced this issue Sep 5, 2024
See #2641

ChainAuthHandlerImpl maintains, for instances of type "any", an index indicating which handler in the list succeeded to authenticate the user.

This index is used in the postAuthentication method to determine which handler int he list should have its postAuthentication method invoked.

When "any" ChainAuthHandlers were nested, the index computed in the top level ChainAuthHandler was used to choose the handlers in the list of nested ChainAuthHandler.

This caused either the wrong handler to be peeked or IndexOutOfBoundsException to be thrown.

With this change, the value put in the RoutingContext is specific to a ChainAuthHandler instance.

Signed-off-by: Thomas Segismont <[email protected]>
@tsegismont
Copy link
Contributor

Fixed by ae41772

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

2 participants