strimzi / strimzi-kafka-oauth

OAuth2 support for Apache Kafka® to work with many OAuth2 authorization servers
Apache License 2.0
144 stars 89 forks source link

JaasClientOauthLoginCallbackHandler not working with Quarkus native #105

Open merlante opened 3 years ago

merlante commented 3 years ago

This issue might live with Quarkus or Graalvm, but I thought I'd start here.

I'm using Oauth authentication (kafka-oauth-client 0.7.1) with KafkaStreams on Quarkus (0.13.0) and the io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler class for kafka-streams.sasl.login.callback.handler.class (config at the end). This setup works, and I can login correctly in jvm mode. However, in native mode it does now work.

When I do:

./mvnw compile quarkus:dev

my app logs in with no problems.

However, when I do (on a mac -- but I have also see this with the container build and a docker run):

./mvnw clean package -Pnative ./target/available-stock-processor-1.0.0-SNAPSHOT-runner

I get the exception, below.

I believe that the issue is related to the use of java.net.URL in JaasClientOauthLoginCallbackHandler/HttpUtil (which is masked somewhat by the "LoginException: java.lang.IllegalArgumentException: Malformed token endpoint url"). I think it may be related or similar to: - https://github.com/quarkusio/quarkus/issues/12447 - https://github.com/oracle/graal/pull/2889 - https://github.com/oracle/graal/issues/1721

Thanks, mark

Stack trace (note I have X'ed out parts of the endpoint url, but it is good):

2021-04-06 14:17:55,621 ERROR [io.qua.run.Application] (main) Failed to start application (with profile prod): javax.security.auth.login.LoginException: java.lang.IllegalArgumentException: Malformed token endpoint url: https://XXXXXXXXXXXXXX/auth/realms/XXXXXXX/protocol/openid-connect/token at io.strimzi.kafka.oauth.common.HttpUtil.request(HttpUtil.java:96) at io.strimzi.kafka.oauth.common.HttpUtil.request(HttpUtil.java:86) at io.strimzi.kafka.oauth.common.HttpUtil.post(HttpUtil.java:62) at io.strimzi.kafka.oauth.common.OAuthAuthenticator.post(OAuthAuthenticator.java:92) at io.strimzi.kafka.oauth.common.OAuthAuthenticator.loginWithClientSecret(OAuthAuthenticator.java:60) at io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler.handleCallback(JaasClientOauthLoginCallbackHandler.java:158) at io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler.handle(JaasClientOauthLoginCallbackHandler.java:138) at org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule.identifyToken(OAuthBearerLoginModule.java:316) at org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule.login(OAuthBearerLoginModule.java:301) at javax.security.auth.login.LoginContext.invoke(LoginContext.java:726) at javax.security.auth.login.LoginContext$4.run(LoginContext.java:665) at javax.security.auth.login.LoginContext$4.run(LoginContext.java:663) at java.security.AccessController.doPrivileged(AccessController.java:147) at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:663) at javax.security.auth.login.LoginContext.login(LoginContext.java:574) at org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin.login(ExpiringCredentialRefreshingLogin.java:204) at org.apache.kafka.common.security.oauthbearer.internals.OAuthBearerRefreshingLogin.login(OAuthBearerRefreshingLogin.java:150) at org.apache.kafka.common.security.authenticator.LoginManager.(LoginManager.java:62) at org.apache.kafka.common.security.authenticator.LoginManager.acquireLoginManager(LoginManager.java:105) at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:158) at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:157) at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:73) at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105) at org.apache.kafka.clients.admin.KafkaAdminClient.createInternal(KafkaAdminClient.java:508) at org.apache.kafka.clients.admin.Admin.create(Admin.java:65) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer.(KafkaStreamsProducer.java:91) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.create(KafkaStreamsProducer_Bean.zig:742) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.create(KafkaStreamsProducer_Bean.zig:762) at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96) at io.quarkus.arc.impl.AbstractSharedContext.access$000(AbstractSharedContext.java:14) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26) at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26) at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69) at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.get(KafkaStreamsProducer_Bean.zig:794) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.get(KafkaStreamsProducer_Bean.zig:810) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.create(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:136) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.create(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:154) at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96) at io.quarkus.arc.impl.AbstractSharedContext.access$000(AbstractSharedContext.java:14) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26) at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26) at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69) at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.get(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:186) at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.get(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:202) at org.acme.services.AvailableStockService_Bean.create(AvailableStockService_Bean.zig:167) at org.acme.services.AvailableStockService_Bean.create(AvailableStockService_Bean.zig:207) at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96) at io.quarkus.arc.impl.AbstractSharedContext.access$000(AbstractSharedContext.java:14) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26) at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26) at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69) at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26) at org.acme.services.AvailableStockService_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.notify(AvailableStockService_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.zig:135) at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:283) at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:268) at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:70) at io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:128) at io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:97) at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(LifecycleEventsBuildStep$startupEvent1144526294.zig:87) at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(LifecycleEventsBuildStep$startupEvent1144526294.zig:40) at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:653) at io.quarkus.runtime.Application.start(Application.java:90) at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:100) at io.quarkus.runtime.Quarkus.run(Quarkus.java:66) at io.quarkus.runtime.Quarkus.run(Quarkus.java:42) at io.quarkus.runtime.Quarkus.run(Quarkus.java:119) at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)

    at javax.security.auth.login.LoginContext.invoke(LoginContext.java:821)
    at javax.security.auth.login.LoginContext$4.run(LoginContext.java:665)
    at javax.security.auth.login.LoginContext$4.run(LoginContext.java:663)
    at java.security.AccessController.doPrivileged(AccessController.java:147)
    at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:663)
    at javax.security.auth.login.LoginContext.login(LoginContext.java:574)
    at org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin.login(ExpiringCredentialRefreshingLogin.java:204)
    at org.apache.kafka.common.security.oauthbearer.internals.OAuthBearerRefreshingLogin.login(OAuthBearerRefreshingLogin.java:150)
    at org.apache.kafka.common.security.authenticator.LoginManager.<init>(LoginManager.java:62)
    at org.apache.kafka.common.security.authenticator.LoginManager.acquireLoginManager(LoginManager.java:105)
    at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:158)
    at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:157)
    at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:73)
    at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105)
    at org.apache.kafka.clients.admin.KafkaAdminClient.createInternal(KafkaAdminClient.java:508)
    at org.apache.kafka.clients.admin.Admin.create(Admin.java:65)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer.<init>(KafkaStreamsProducer.java:91)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.create(KafkaStreamsProducer_Bean.zig:742)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.create(KafkaStreamsProducer_Bean.zig:762)
    at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96)
    at io.quarkus.arc.impl.AbstractSharedContext.access$000(AbstractSharedContext.java:14)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26)
    at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
    at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
    at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.get(KafkaStreamsProducer_Bean.zig:794)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_Bean.get(KafkaStreamsProducer_Bean.zig:810)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.create(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:136)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.create(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:154)
    at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96)
    at io.quarkus.arc.impl.AbstractSharedContext.access$000(AbstractSharedContext.java:14)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26)
    at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
    at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
    at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.get(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:186)
    at io.quarkus.kafka.streams.runtime.KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.get(KafkaStreamsProducer_ProducerMethod_getKafkaStreams_7790cbc0b67cec783271544399d0fa71077d93dc_Bean.zig:202)
    at org.acme.services.AvailableStockService_Bean.create(AvailableStockService_Bean.zig:167)
    at org.acme.services.AvailableStockService_Bean.create(AvailableStockService_Bean.zig:207)
    at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96)
    at io.quarkus.arc.impl.AbstractSharedContext.access$000(AbstractSharedContext.java:14)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29)
    at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26)
    at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
    at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
    at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26)
    at org.acme.services.AvailableStockService_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.notify(AvailableStockService_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.zig:135)
    at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:283)
    at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:268)
    at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:70)
    at io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:128)
    at io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:97)
    at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(LifecycleEventsBuildStep$startupEvent1144526294.zig:87)
    at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(LifecycleEventsBuildStep$startupEvent1144526294.zig:40)
    at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:653)
    at io.quarkus.runtime.Application.start(Application.java:90)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:100)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
    at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)

Quarkus oauth config (from application.properties):

quarkus.kafka-streams.security.protocol=SASL_SSL quarkus.kafka-streams.sasl.mechanism=OAUTHBEARER quarkus.kafka-streams.sasl.jaas-config=org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required \ oauth.client.id="${USER}" \ oauth.client.secret="${PASSWORD}" \ oauth.token.endpoint.uri="${ENDPOINT_URL}" ; kafka-streams.sasl.login.callback.handler.class=io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler

mstruk commented 3 years ago

I would check if there are quotes in the url or newline characters, or any special characters like when you copy paste from some chat app and discover that '-' is really an ASCII minus char.

merlante commented 3 years ago

@mstruk There are '-'s in the url, but, like I said, the url is not reported as "malformed" when the app is run in jvm mode. The exception is only thrown in native mode.

Either the string is parsed differently in native mode or something like the issue with java.net.URL I cited is at play.

mstruk commented 3 years ago

I see. Yes, it happening only with Quarkus Native is suspicious, and most likely a Quarkus issue.

It is unusual that MalformedURLException would be thrown in such a case, rather than some RuntimeException or an Error. Maybe you would get more information if the cause exception was attached when rethrowing here.

Maybe try change this line to:

throw new IllegalArgumentException("Malformed token endpoint url: ", e);

Rebuild strimzi-kafka-oauth and package your build with your Kafka broker. Maybe that will display the cause and help you make some progress.

merlante commented 3 years ago

Apologies for not following up in a while. I'm having some issues making that change because when I use my own builds of strimzi-kafka-oauth with my app, ./mvnw clean package -Pnative does not even complete.

I get the following exception when I build my app against my own build of strimzi-kafka-oauth from main (HEAD) and from the 0.7.1 tag (and also with the community 0.8.1 version build in Central), that I don't get with the "official" build of 0.7.1:

Error: com.oracle.svm.hosted.substitute.DeletedElementException: Unsupported method java.lang.ClassLoader.defineClass(String, byte[], int, int) is reachable
To diagnose the issue, you can add the option --report-unsupported-elements-at-runtime. The unsupported element is then reported at run time when it is accessed the first time.
Detailed message:
Trace: 
        at parsing com.nimbusds.jose.shaded.asm.DynamicClassLoader.defineClass(DynamicClassLoader.java:87)
Call path from entry point to com.nimbusds.jose.shaded.asm.DynamicClassLoader.defineClass(String, byte[]): 
        at com.nimbusds.jose.shaded.asm.DynamicClassLoader.defineClass(DynamicClassLoader.java:82)
        at com.nimbusds.jose.shaded.asm.BeansAccessBuilder.bulid(BeansAccessBuilder.java:330)
        at com.nimbusds.jose.shaded.asm.BeansAccess.get(BeansAccess.java:121)
        at com.nimbusds.jose.shaded.json.reader.BeansWriterASM.writeJSONString(BeansWriterASM.java:17)
        at com.nimbusds.jose.shaded.json.JSONValue.writeJSONString(JSONValue.java:599)
        at com.nimbusds.jose.shaded.json.reader.JsonWriter.writeJSONKV(JsonWriter.java:392)
        at com.nimbusds.jose.shaded.json.reader.JsonWriter$7.writeJSONString(JsonWriter.java:145)
        at com.nimbusds.jose.shaded.json.reader.JsonWriter$7.writeJSONString(JsonWriter.java:128)
        at com.nimbusds.jose.shaded.json.JSONObject.writeJSON(JSONObject.java:178)
        at com.nimbusds.jose.shaded.json.JSONObject.toJSONString(JSONObject.java:66)
        at com.nimbusds.jose.shaded.json.JSONObject.toString(JSONObject.java:264)
        at java.lang.String.valueOf(String.java:2951)
        at java.io.PrintStream.print(PrintStream.java:745)
        at java.io.PrintStream.println(PrintStream.java:882)
        at com.oracle.svm.jni.functions.JNIFunctions.ExceptionDescribe(JNIFunctions.java:757)
        at com.oracle.svm.core.code.IsolateEnterStub.JNIFunctions_ExceptionDescribe_b5412f7570bccae90b000bc37855f00408b2ad73(generated:0)

...

Caused by: com.oracle.svm.hosted.substitute.DeletedElementException: Unsupported method java.lang.ClassLoader.defineClass(String, byte[], int, int) is reachable
To diagnose the issue, you can add the option --report-unsupported-elements-at-runtime. The unsupported element is then reported at run time when it is accessed the first time.
        at com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor.lookup(AnnotationSubstitutionProcessor.java:187)
        at com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor$ChainedSubstitutionProcessor.lookup(SubstitutionProcessor.java:128)
        at com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor$ChainedSubstitutionProcessor.lookup(SubstitutionProcessor.java:128)
        at com.oracle.graal.pointsto.meta.AnalysisUniverse.lookupAllowUnresolved(AnalysisUniverse.java:409)
        at com.oracle.graal.pointsto.infrastructure.WrappedConstantPool.lookupMethod(WrappedConstantPool.java:125)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupMethodInPool(BytecodeParser.java:4345)
        at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase$SharedBytecodeParser.lookupMethodInPool(SharedGraphBuilderPhase.java:119)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.lookupMethod(BytecodeParser.java:4339)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.genInvokeVirtual(BytecodeParser.java:1700)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5404)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3436)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:3243)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:1109)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:1003)
        at jdk.internal.vm.compiler/org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:84)
        at com.oracle.svm.hosted.phases.SharedGraphBuilderPhase.run(SharedGraphBuilderPhase.java:76)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.run(Phase.java:49)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:212)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
        at jdk.internal.vm.compiler/org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
        at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:223)
        at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:357)
        at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:313)
        at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:302)
        at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:103)
        at com.oracle.graal.pointsto.DefaultAnalysisPolicy$DefaultVirtualInvokeTypeFlow.onObservedUpdate(DefaultAnalysisPolicy.java:227)
        at com.oracle.graal.pointsto.flow.TypeFlow.notifyObservers(TypeFlow.java:470)
        at com.oracle.graal.pointsto.flow.TypeFlow.update(TypeFlow.java:542)
        at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:547)
        at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:173)
        at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
        ... 5 more
Error: Image build request failed with exit status 1

To build kafka-oauth-client, I did:

git clone git@github.com:strimzi/strimzi-kafka-oauth.git
cd strimzi-kafka-oauth
git checkout 0.7.1
mvn clean install -Dmaven.test.skip=true

I also had to set <failOnWarnings>false</failOnWarnings> for maven-javadoc-plugin.

In the pom.xml for my app I added:

<dependency>
      <groupId>io.strimzi</groupId>
      <artifactId>kafka-oauth-client</artifactId>
      <!--<version>0.7.1</version>-->
      <version>1.0.0-SNAPSHOT</version>
      <scope>runtime</scope>
    </dependency>

If I can get past this, and reproduce my original Exception with my own build, then I should be able to follow your suggestion.

merlante commented 3 years ago

I managed to resolve my issue (now hidden) earlier.

I rebuilt my app in native with @mstruk's suggested change in strimzi-kafka-oauth, re-ran the executable, and it gave me the following exception:

2021-06-25 16:43:57,143 ERROR [io.qua.run.Application] (main) Failed to start application (with profile prod): javax.security.auth.login.LoginException: java.lang.IllegalArgumentException: Malformed token endpoint url: java.net.MalformedURLException: Accessing an URL protocol that was not enabled. The URL protocol https is supported but not enabled by default. It must be enabled by adding the --enable-url-protocols=https option to the native-image command.
        at io.strimzi.kafka.oauth.common.HttpUtil.request(HttpUtil.java:96)
        at io.strimzi.kafka.oauth.common.HttpUtil.request(HttpUtil.java:86)
        at io.strimzi.kafka.oauth.common.HttpUtil.post(HttpUtil.java:62)
        at io.strimzi.kafka.oauth.common.OAuthAuthenticator.post(OAuthAuthenticator.java:92)
        at io.strimzi.kafka.oauth.common.OAuthAuthenticator.loginWithClientSecret(OAuthAuthenticator.java:60)
        at io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler.handleCallback(JaasClientOauthLoginCallbackHandler.java:158)
        at io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler.handle(JaasClientOauthLoginCallbackHandler.java:138)
        at org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule.identifyToken(OAuthBearerLoginModule.java:316)
        at org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule.login(OAuthBearerLoginModule.java:301)
        at javax.security.auth.login.LoginContext.invoke(LoginContext.java:726)
        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:665)
        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:663)
        at java.security.AccessController.doPrivileged(AccessController.java:147)
        at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:663)
        at javax.security.auth.login.LoginContext.login(LoginContext.java:574)
        at org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin.login(ExpiringCredentialRefreshingLogin.java:204)
        at org.apache.kafka.common.security.oauthbearer.internals.OAuthBearerRefreshingLogin.login(OAuthBearerRefreshingLogin.java:150)
        at org.apache.kafka.common.security.authenticator.LoginManager.<init>(LoginManager.java:62)
        at org.apache.kafka.common.security.authenticator.LoginManager.acquireLoginManager(LoginManager.java:105)
        at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:158)
        at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:157)
        at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:73)
        at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105)
        at org.apache.kafka.clients.consumer.KafkaConsumer.<init>(KafkaConsumer.java:734)
        at io.smallrye.reactive.messaging.kafka.impl.KafkaSource.createKafkaConsumer(KafkaSource.java:250)
        at io.smallrye.reactive.messaging.kafka.impl.KafkaSource.<init>(KafkaSource.java:141)
        at io.smallrye.reactive.messaging.kafka.KafkaConnector.getPublisherBuilder(KafkaConnector.java:161)
        at io.smallrye.reactive.messaging.kafka.KafkaConnector_ClientProxy.getPublisherBuilder(KafkaConnector_ClientProxy.zig:277)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.createPublisherBuilder(ConfiguredChannelFactory.java:190)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.register(ConfiguredChannelFactory.java:153)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.initialize(ConfiguredChannelFactory.java:125)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory_ClientProxy.initialize(ConfiguredChannelFactory_ClientProxy.zig:189)
        at java.util.Iterator.forEachRemaining(Iterator.java:133)
        at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
        at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
        at io.smallrye.reactive.messaging.extension.MediatorManager.start(MediatorManager.java:181)
        at io.smallrye.reactive.messaging.extension.MediatorManager_ClientProxy.start(MediatorManager_ClientProxy.zig:282)
        at io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle.onApplicationStart(SmallRyeReactiveMessagingLifecycle.java:40)
        at io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle_Observer_onApplicationStart_4e8937813d9e8faff65c3c07f88fa96615b70e70.notify(SmallRyeReactiveMessagingLifecycle_Observer_onApplicationStart_4e8937813d9e8faff65c3c07f88fa96615b70e70.zig:111)
        at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:283)
        at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:268)
        at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:70)
        at io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:128)
        at io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:97)
        at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(LifecycleEventsBuildStep$startupEvent1144526294.zig:87)
        at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(LifecycleEventsBuildStep$startupEvent1144526294.zig:40)
        at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:686)
        at io.quarkus.runtime.Application.start(Application.java:90)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:100)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
        at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)

        at javax.security.auth.login.LoginContext.invoke(LoginContext.java:821)
        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:665)
        at javax.security.auth.login.LoginContext$4.run(LoginContext.java:663)
        at java.security.AccessController.doPrivileged(AccessController.java:147)
        at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:663)
        at javax.security.auth.login.LoginContext.login(LoginContext.java:574)
        at org.apache.kafka.common.security.oauthbearer.internals.expiring.ExpiringCredentialRefreshingLogin.login(ExpiringCredentialRefreshingLogin.java:204)
        at org.apache.kafka.common.security.oauthbearer.internals.OAuthBearerRefreshingLogin.login(OAuthBearerRefreshingLogin.java:150)
        at org.apache.kafka.common.security.authenticator.LoginManager.<init>(LoginManager.java:62)
        at org.apache.kafka.common.security.authenticator.LoginManager.acquireLoginManager(LoginManager.java:105)
        at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:158)
        at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:157)
        at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:73)
        at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105)
        at org.apache.kafka.clients.consumer.KafkaConsumer.<init>(KafkaConsumer.java:734)
        at io.smallrye.reactive.messaging.kafka.impl.KafkaSource.createKafkaConsumer(KafkaSource.java:250)
        at io.smallrye.reactive.messaging.kafka.impl.KafkaSource.<init>(KafkaSource.java:141)
        at io.smallrye.reactive.messaging.kafka.KafkaConnector.getPublisherBuilder(KafkaConnector.java:161)
        at io.smallrye.reactive.messaging.kafka.KafkaConnector_ClientProxy.getPublisherBuilder(KafkaConnector_ClientProxy.zig:277)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.createPublisherBuilder(ConfiguredChannelFactory.java:190)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.register(ConfiguredChannelFactory.java:153)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.initialize(ConfiguredChannelFactory.java:125)
        at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory_ClientProxy.initialize(ConfiguredChannelFactory_ClientProxy.zig:189)
        at java.util.Iterator.forEachRemaining(Iterator.java:133)
        at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
        at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
        at io.smallrye.reactive.messaging.extension.MediatorManager.start(MediatorManager.java:181)
        at io.smallrye.reactive.messaging.extension.MediatorManager_ClientProxy.start(MediatorManager_ClientProxy.zig:282)
        at io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle.onApplicationStart(SmallRyeReactiveMessagingLifecycle.java:40)
        at io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle_Observer_onApplicationStart_4e8937813d9e8faff65c3c07f88fa96615b70e70.notify(SmallRyeReactiveMessagingLifecycle_Observer_onApplicationStart_4e8937813d9e8faff65c3c07f88fa96615b70e70.zig:111)
        at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:283)
        at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:268)
        at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:70)
        at io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:128)
        at io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:97)
        at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(LifecycleEventsBuildStep$startupEvent1144526294.zig:87)
        at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(LifecycleEventsBuildStep$startupEvent1144526294.zig:40)
        at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:686)
        at io.quarkus.runtime.Application.start(Application.java:90)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:100)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
        at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)

So the malformedurlexception was indeed a red herring, and https needs to be enabled for the native build instead.

Following some quick googling I added quarkus.ssl.native=true to my application.properties, rebuilt in native, and now the issue appears to be resolved.

It feels like I shouldn't have to do this explicitly though. It would be nice if the extension set this behind the scenes. EDIT: I forgot that it's not a quarkus extension, but just a pom dependency. Still though. :)

(I've been drawing inspiration from https://quarkus.io/guides/native-and-ssl)

merlante commented 3 years ago

@mstruk Do you think there is an action required here? Even just to document somewhere that ssl must be enabled for native support for this callback class?

scholzj commented 3 years ago
mstruk commented 3 years ago

If you look at it as Quarkus native build support for Strimzi client, then I agree with @scholzj . But it's actually Quarkus native build support for Kafka client if I'm not mistaken. And Strimzi OAuth may be out of their scope as one of many possible extensions for client auth. I don't think it would hurt to document it here, but then it would best be documented as part of the Quarkus native example using strimzi-kafka-oauth. Also since client dependencies may change with versions as well and it we may be in a better position to have the most up-to-date docs / examples for using it with Quarkus native / GraalVM.

scholzj commented 3 years ago

I'm not sure you want to maintain Quarkus examples and docs here. Apart from the effort of maintaining it, it also opens the pandora box of having the same for other frameworks such as Spring, Akka etc.

merlante commented 3 years ago

I think I first came across the "kafka-oauth-client" in the Quarkus - Using Apache Kafka with Reactive Messaging guide. There is a section, "Authenticating with OAuth", where you are told to add kafka-oauth-client to your pom and add kafka.sasl.login.callback.handler.class=io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler in your jaas config. If you just follow those steps, and build your app in native mode, auth fails with a misleading exception: "LoginException: java.lang.IllegalArgumentException: Malformed token endpoint url".

So at the very least, that guide could do with a change, because elsewhere in the same guide, steps for building the native application are given and there is no mention of quarkus.ssl.native=true in the case of Oauth being used.

In my opinion, there is also a good case for improving the exception message, since the error message that it wraps, "Accessing an URL protocol that was not enabled. The URL protocol https is supported but not enabled by default. It must be enabled by adding the --enable-url-protocols=https option to the native-image command" is reasonably clear.

scholzj commented 3 years ago

The documentation you linked is Quarkus documentation. So you probably should open an issue or PR in Quarkus to improve it. Also, please keep in mind that this is a general library - it is not part of Quarkus. We cannot make the exceptions to fit Quarkus - this library is used in all kind of environments and frameworks.

mstruk commented 3 years ago

In that case the best strategy indeed seems like working with Quarkus team to make sure their existing example works with Strimzi Kafka OAuth. Let's leave this issue open as a TODO for me :)

merlante commented 3 years ago

Thanks @scholzj and @mstruk.

You might still want to do something with the exception, though, which is strimzi code. :)

The exception I got:

Failed to start application (with profile prod): javax.security.auth.login.LoginException: java.lang.IllegalArgumentException: Malformed token endpoint url: https://XXXXXXXXXXXXXX/auth/realms/XXXXXXX/protocol/openid-connect/token
at io.strimzi.kafka.oauth.common.HttpUtil.request(HttpUtil.java:96)

versus what you get when you print the underlying exception:

Failed to start application (with profile prod): javax.security.auth.login.LoginException: java.lang.IllegalArgumentException: Malformed token endpoint url: java.net.MalformedURLException: Accessing an URL protocol that was not enabled. The URL protocol https is supported but not enabled by default. It must be enabled by adding the --enable-url-protocols=https option to the native-image command.
at io.strimzi.kafka.oauth.common.HttpUtil.request(HttpUtil.java:96)

The underlying exception is right to the point, whereas the existing message is misleading imho, and you could spend a lot of time trying to figure out how your url is incorrectly formatted.

mstruk commented 3 years ago

You might still want to do something with the exception, though, which is strimzi code. :)

True. I will do a PR for that. Thanks for pointing it out.

merlante commented 3 years ago

@mstruk FYI, I have created a PR to fix the Quarkus guide: https://github.com/quarkusio/quarkusio.github.io/pull/1063. Feel free to close this when you are happy.

mstruk commented 3 years ago

See https://github.com/quarkusio/quarkus/issues/18398#issuecomment-921712179 for how to fix the issue with native build.