Encountering problems using Java SDK authenticating with an access key (Http2Exception)

Hi all,

currently I am trying to integrate TTN’s SDK (1) into my Java application.
The goal is to use the Application Manager API over gRPC with TLS authenticating with an access key.

This is the code I am using:

ApplicationAccessKey tokenProvider = new ApplicationAccessKey(applicationKey);
AccessKey token                    = tokenProvider.getToken();
Discovery discoveryServer          = Discovery.getDefault();
Handler handler                    = discoveryServer.getHandler(token, handlerId);

The variable applicationKey begins with “ttn-account-v2.” and handlerId is “ttn-handler-eu”. However, changing both to random Strings does not change the fact that the following exceptions are raised in the last line:

java.lang.RuntimeException: java.util.concurrent.ExecutionException: io.grpc.StatusRuntimeException: INTERNAL
        at rx.exceptions.Exceptions.propagate(Exceptions.java:58)
        at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:464)
        at rx.observables.BlockingObservable.single(BlockingObservable.java:341)
        at org.thethingsnetwork.management.sync.Discovery.getHandler(Discovery.java:77)
        [STACK TRACE SHORTENED MANUALLY]
Caused by: java.util.concurrent.ExecutionException: io.grpc.StatusRuntimeException: INTERNAL
        at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:518)
        at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:497)
        at rx.internal.operators.OnSubscribeToObservableFuture$ToObservableFuture.call(OnSubscribeToObservableFuture.java:74)
        at rx.internal.operators.OnSubscribeToObservableFuture$ToObservableFuture.call(OnSubscribeToObservableFuture.java:43)
        at rx.Observable.unsafeSubscribe(Observable.java:10346)
        at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100)
        at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:230)
        at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
        at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
        at java.util.concurrent.FutureTask.run(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)
Caused by: io.grpc.StatusRuntimeException: INTERNAL
        at io.grpc.Status.asRuntimeException(Status.java:540)
        at io.grpc.stub.ClientCalls$UnaryStreamToFuture.onClose(ClientCalls.java:439)
        at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:428)
        at io.grpc.internal.ClientCallImpl.access$100(ClientCallImpl.java:76)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:514)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$700(ClientCallImpl.java:431)
        at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:546)
        at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:52)
        at io.grpc.internal.SerializingExecutor$TaskRunner.run(SerializingExecutor.java:152)
        ... 3 more
Caused by: io.netty.handler.codec.http2.Http2Exception: First received frame was not SETTINGS. Hex dump for first 5 bytes: 1503010002
        at io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:85)
        at io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.verifyFirstFrameIsSettings(Http2ConnectionHandler.java:309)
        at io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:217)
        at io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:401)
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:411)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:248)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:642)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:565)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:479)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:441)
        at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
        ... 1 more

Research (2) revealed that someone encountered the same problem with the same exceptions. Note that even the stated bytes in the following exception’s line are the same:

Caused by: io.netty.handler.codec.http2.Http2Exception: First received frame was not SETTINGS. Hex dump for first 5 bytes: 1503010002

Unfortunately, the solution presented in (2) is very short and only says that this exception is raised when the server is using TLS but the client is not.

Assuming that my code using TTN’s JDK is correct and the problem generating the exceptions is the same as in (2), why is TTN’s JDK not using TLS and how can I fix it?

Please let me know if you have any ideas or notice any mistakes in my code.
Thank you in advance!

Best regards,

Florian

(1) https://github.com/TheThingsNetwork/java-app-sdk
(2) https://github.com/grpc/grpc-java/issues/4063

Push and update.

When I use the following code authenticating with clientID and clientSecret the result is an exception saying that I am unauthorised, which is reasonable because I do not have any clientID or clientSecret.

ApplicationPassword tokenProvider   = new ApplicationPassword(config.getApplicationId(), config.getApplicationKey(), config.getClientId(), config.getClientSecret());
JsonWebToken token                  = tokenProvider.getToken();
Discovery discoveryServer           = Discovery.getDefault();
Handler handler                     = discoveryServer.getHandler(token, handlerId);

Concluding, the http2exception I encountered first does not appear, which is why I think the code presented in my first post is faulty.

According to (1), authenticating with an access key should work.

Please let me know if you have any ideas. Thanks!

Best regards

Florian

(1) https://www.thethingsnetwork.org/docs/applications/manager/authentication.html#access-key

Push and update.

I think I found the reason why the http2exception is thrown.

When calling
Discovery discoveryServer = Discovery.getDefault();
the following method is called in class org.thethingsnetwork.management.async.AsyncDiscovery:

public static Observable<AsyncDiscovery> from(String _host, int _port) {
        return Observable
                .create((Subscriber<? super AsyncDiscovery> t) -> {
                    try {
                        ManagedChannel ch = ManagedChannelBuilder
                                .forAddress(_host, _port)
                                .usePlaintext(true)
                                .build();
                        DiscoveryGrpc.DiscoveryFutureStub stub1 = DiscoveryGrpc.newFutureStub(ch);
                        t.onNext(new AsyncDiscovery(stub1));
                        t.onCompleted();
                    } catch (Exception ex) {
                        t.onError(ex);
                    }
                });
    }

The ManagedChannel is created with .usePlaintext(true) meaning that no TLS is used on client-side. According to link (1) of my first post, the server is using TLS and therefore the exception is thrown.

It seems like TTN’s Java SDK is not up to date (the last commit is from 23 Apr 2017).

Best regards

Florian