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

Calls made using Keycloak admin client fail with "authHeader" is null when using resteasy-reactive-jsonb #30089

Closed
garthberry opened this issue Dec 28, 2022 · 15 comments

Comments

@garthberry
Copy link

garthberry commented Dec 28, 2022

Describe the bug

When using resteasy-reactive-jsonb calls made using the Keycloak admin client will fail with an NPE

Expected behavior

Behaviour should be the same as when using resteasy-reactive-jackson

Actual behavior

Calls made using the Keycloak admin client fail with the following exception.

`
java.lang.NullPointerException: Cannot invoke "String.startsWith(String)" because "authHeader" is null

at org.keycloak.admin.client.resource.BearerAuthFilter.filter(BearerAuthFilter.java:53)
at org.jboss.resteasy.reactive.client.handlers.ClientRequestFilterRestHandler.handle(ClientRequestFilterRestHandler.java:25)
at org.jboss.resteasy.reactive.client.handlers.ClientRequestFilterRestHandler.handle(ClientRequestFilterRestHandler.java:10)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.invokeHandler(AbstractResteasyReactiveContext.java:229)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
at org.jboss.resteasy.reactive.client.impl.AsyncInvokerImpl.performRequestInternal(AsyncInvokerImpl.java:265)
at org.jboss.resteasy.reactive.client.impl.AsyncInvokerImpl.performRequestInternal(AsyncInvokerImpl.java:255)
at org.jboss.resteasy.reactive.client.impl.AsyncInvokerImpl.method(AsyncInvokerImpl.java:200)
at org.jboss.resteasy.reactive.client.impl.InvocationBuilderImpl.method(InvocationBuilderImpl.java:329)
at org.keycloak.admin.client.resource.RolesResourcecec060e01a4e8719a5229b25492913298e3846fc51.list(Unknown Source)
at RolesResourceTest.testGetRoles(RolesResourceTest.java:15)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:997)
at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:816)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

`

How to Reproduce?

Reproducer attached.

security-keycloak-admin-client-jsonb.tar.gz

Output of uname -a or ver

Darwin pingguopao.orchard.home 22.2.0 Darwin Kernel Version 22.2.0: Fri Nov 11 02:03:51 PST 2022; root:xnu-8792.61.2~4/RELEASE_ARM64_T6000 arm64

Output of java -version

openjdk version "17.0.5" 2022-10-18 OpenJDK Runtime Environment Homebrew (build 17.0.5+0) OpenJDK 64-Bit Server VM Homebrew (build 17.0.5+0, mixed mode, sharing)

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.15.1.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Maven home: apache-maven-3.8.6 Java version: 17.0.5, vendor: Homebrew, runtime: /opt/homebrew/Cellar/openjdk@17/17.0.5/libexec/openjdk.jdk/Contents/Home Default locale: en_GB, platform encoding: UTF-8 OS name: "mac os x", version: "13.1", arch: "aarch64", family: "mac"

Additional information

The issue can also be reproduced when using the non-reactive variants of resteasy and Keycloak admin client.

@garthberry garthberry added the kind/bug Something isn't working label Dec 28, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Dec 28, 2022

/cc @FroMage(resteasy-reactive), @geoand(resteasy-reactive), @pedroigor(keycloak), @sberyozkin(keycloak), @stuartwdouglas(resteasy-reactive)

@sberyozkin
Copy link
Member

@garthberry Given that it does not work with non-reactive variants as well, can you please confirm that keycloak-admin-client can work with JSONB at all, outside of Quarkus ?

@garthberry
Copy link
Author

@sberyozkin I can't comment outside of Quarkus, but, we have been using the non-reactive variant with Quarkus and JSONB from at least 1.13.7.Final up to and including version 2.13.6.Final.

I have also confirmed using the reproducer that non-reactive Keycloak admin client with JSONB works with Quarkus versions up to and including 2.14.0.Final. From Quarkus 2.14.1.Final onwards the NPE is thrown with both reactive and non-reactive variants.

The NPE occurs with reactive variant and JSONB from at least Quarkus 2.8.0.Final.

@sberyozkin
Copy link
Member

sberyozkin commented Dec 28, 2022

@garthberry Thanks, I've looked at various commits, and I have no idea what might've introduced a regression, for example, given:

I have also confirmed using the reproducer that non-reactive Keycloak admin client with JSONB works with Quarkus versions up to and including 2.14.0.Final. From Quarkus 2.14.1.Final onwards the NPE is thrown with both reactive and non-reactive variants.

There is only one commit in the whole collection of resteasy-classic extensions (i.e non-reactive variants like resteasy-client, reasteasy-jsonb-provider, etc) in 2.14.1.Final
https://github.com/quarkusio/quarkus/commits/2.14.1.Final/extensions/resteasy-classic
a0f316d,

No changes in the keycloak-admin-client-* extensions between 2.14.0.Final & 2.14.1.Final

@garthberry
Copy link
Author

garthberry commented Dec 29, 2022

@sberyozkin I have tracked down the commit responsible for the regression in the non-reactive variant.

a74dd7125aea0ae35aba15a506f861058a83d976

Using a local build of both 2.14.1.Final and 2.15.1.Final with that commit reverted resolves the NPE when using JSONB.

It appears that the root cause may be the same for both variants.

@sberyozkin
Copy link
Member

@garthberry But it is all related to Jackson, not related to JSONB.
It might be just an ordering issue that now a JSONB provider is selected first.
Can you please check, in a debug mode, which JSON provider is actually used when no NPE happens ? I.e, can you confirm it is in fact a JSONB provider which is used ?

@garthberry
Copy link
Author

@sberyozkin Looking at the Resteasy Client Proxy for org.keycloak.admin.client.token.TokenService for non-reactive variant using JSONB provider in 2.14.0.Final the clientHelper messageBodyReader for media type "application/json" is JacksonProvider. Whereas 2.14.1.Final it is set to JsonBindingProvider, however, the jsonbObj attribute is null.

Interestingly the reactive variant seems to have the resourceReader is an instance of ClientJacksonMessageBodyReader when the JSONB provider is set. When the Jackson provider is set the resourceReader is an instance of ServerJacksonMessageBodyReader.

It seems there are separate issues for each resteasy variant resulting in an NPE.

@sberyozkin
Copy link
Member

@garthberry I'm pretty sure now that it is all down to the provider ordering. Something, example, Java Map implementation, has changed, in 2.14.1.Final, and now JSONB Provider either is selected first (Resteasy Classic) or affects the selection of other providers (Resteasy Reactive).

I think the bottom line is that using JSONB provider while also using Keycloak Admin Client API which needs Jackson can lead to unpredictable results/bugs.

The easiest option, if it is possible for your application, is move from JSONB to using Jackson only.

If that does not work, then I can suggest, assuming JSONB is only used in the server scope, is to try and register it in the server scope only, so that it does not interfere with the client-scope JSON processing (ex, that of keycloak-admin-client and Jackson processing the client requests/responses). for custom providers one should be able to do it indirectly by registering Feature, not sure how to handle it for built-in providers.

Hi @geoand @FroMage @gsmet Can you recommend something re how to get a provider like reactive JSONB scoped to the server request/response processing only so that it does not interfere with the client side Jackson processing ?

@garthberry
Copy link
Author

@sberyozkin Thank you, I have started investigating moving from JSONB to Jackson. Your assumption is correct, JSONB is only used in the server scope, so if it is possible to restrict JSONB to server request/response processing only that might be another viable workaround.

@geoand
Copy link
Contributor

geoand commented Jan 3, 2023

Hi @geoand @FroMage @gsmet Can you recommend something re how to get a provider like reactive JSONB scoped to the server request/response processing only so that it does not interfere with the client side Jackson processing ?

I know what's going on, let me open a PR to address it

@geoand
Copy link
Contributor

geoand commented Jan 3, 2023

#30146 takes care of it

geoand added a commit to geoand/quarkus that referenced this issue Jan 3, 2023
geoand added a commit to geoand/quarkus that referenced this issue Jan 3, 2023
geoand added a commit to geoand/quarkus that referenced this issue Jan 3, 2023
geoand added a commit to geoand/quarkus that referenced this issue Jan 3, 2023
@sberyozkin
Copy link
Member

sberyozkin commented Jan 3, 2023

Thanks Georgios, @garthberry Can you please check @geoand's PR and see if the other alternative related to the provider scopes also works now (unless you have already migrated to Jackson :-)) ?

geoand added a commit to geoand/quarkus that referenced this issue Jan 3, 2023
@garthberry
Copy link
Author

@sberyozkin The Jackson migration is underway, nonetheless, I checked the PR against my reproducer and it works.

When using reactive JSONB I can see that both JSONB and Jackson reactive providers are registered. When using reactive Jackson I only see the one provider registered.

Thank you Georgios!

sberyozkin added a commit that referenced this issue Jan 3, 2023
Properly segregate Json MessageBodyReader/Writer classes for server and client
@sberyozkin
Copy link
Member

Thanks @garthberry

@geoand
Copy link
Contributor

geoand commented Jan 4, 2023

Thanks for the collaboration everyone!

gsmet pushed a commit to gsmet/quarkus that referenced this issue Jan 5, 2023
ebullient pushed a commit to maxandersen/quarkus that referenced this issue Jan 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants