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

Android only - Returning a TokenDetails with 'issued' and 'expires' values in epoch milliseconds throws java error #356

Closed
gabrielginter opened this issue Mar 25, 2022 · 3 comments · Fixed by #358
Assignees
Labels
bug Something isn't working. It's clear that this does need to be fixed.

Comments

@gabrielginter
Copy link

gabrielginter commented Mar 25, 2022

Setup:

  • Device: Pixel 3XL
  • Android: 11
  • Flutter: 2.10.3
  • Ably package: 1.2.12

Configuration:
Ably with token authentication

{{authCallback}} property in {{ClientOptions}} pointing to a function that returns a {{TokenDetails}} object. As per documentation the object contains {{issued}} and {{expires}} properties set to an epoch timestamp in milliseconds (along the rest of the properties), for clarification, the following code works flawlessly on iOS, and its equivalent in the Javascript SDK also works correctly.

Code:

{code:dart}
import 'package:ably_flutter/ably_flutter.dart' as Ably;

String _clientId = '';
Future<Ably.TokenDetails> _getTokenDetails(Ably.TokenParams params) async {
final result = await MyAPIController.getAblyAuthTokenFor(_clientId);

return Ably.TokenDetails(
  result.data.token,
  issued: result.data.issued,  // result.data.issued is an int with epoch timestamp in milliseconds (e.g.: 1648218289847)
  expires: result.data.expires,  // result.data.expires same as result.data.issued but in the future
  clientId: result.data.clientId,
  capability: result.data.capability,
);

}

void initializeAbly(String clientId) {
_clientId = clientId;

final clientOptions = Ably.ClientOptions()
  ..clientId = _clientId
  ..tls = true
  ..useTokenAuth = true
  ..authCallback = _getTokenDetails;

_realtime = Ably.Realtime(options: clientOptions);
_realtime.connection
    .on(Ably.ConnectionEvent.connected)
    .listen((Ably.ConnectionStateChange stateChange) {
 // SUBSCRIBE TO CHANNELS
  }
});

}
{code}
Error::

{code}
E/MethodChannel#io.ably.flutter.plugin( 3539): Failed to handle method call result
E/MethodChannel#io.ably.flutter.plugin( 3539): java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.AblyMessageCodec.lambda$decodeTokenDetails$32(AblyMessageCodec.java:354)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.-$$Lambda$AblyMessageCodec$EZVHcAHH13F6P7lENmPZqTXnZg8.accept(Unknown Source:2)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.AblyMessageCodec.readValueFromJson(AblyMessageCodec.java:160)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.AblyMessageCodec.decodeTokenDetails(AblyMessageCodec.java:354)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.AblyMessageCodec.access$2300(AblyMessageCodec.java:47)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.AblyMessageCodec$1.lambda$new$5(AblyMessageCodec.java:102)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.-$$Lambda$AblyMessageCodec$1$DqnBUFL6jWi43rRYX8P4My0ezrE.decode(Unknown Source:2)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.AblyMessageCodec$CodecPair.decode(AblyMessageCodec.java:80)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.ably.flutter.plugin.AblyMessageCodec.readValueOfType(AblyMessageCodec.java:152)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.flutter.plugin.common.StandardMessageCodec.readValue(StandardMessageCodec.java:333)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.flutter.plugin.common.StandardMethodCodec.decodeEnvelope(StandardMethodCodec.java:107)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.flutter.plugin.common.MethodChannel$IncomingResultHandler.reply(MethodChannel.java:239)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.flutter.embedding.engine.dart.DartMessenger.handlePlatformMessageResponse(DartMessenger.java:376)
E/MethodChannel#io.ably.flutter.plugin( 3539): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessageResponse(FlutterJNI.java:993)
E/MethodChannel#io.ably.flutter.plugin( 3539): at android.os.MessageQueue.nativePollOnce(Native Method)
E/MethodChannel#io.ably.flutter.plugin( 3539): at android.os.MessageQueue.next(MessageQueue.java:335)
E/MethodChannel#io.ably.flutter.plugin( 3539): at android.os.Looper.loopOnce(Looper.java:161)
E/MethodChannel#io.ably.flutter.plugin( 3539): at android.os.Looper.loop(Looper.java:288)
E/MethodChannel#io.ably.flutter.plugin( 3539): at android.app.ActivityThread.main(ActivityThread.java:7842)
E/MethodChannel#io.ably.flutter.plugin( 3539): at java.lang.reflect.Method.invoke(Native Method)
E/MethodChannel#io.ably.flutter.plugin( 3539): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/MethodChannel#io.ably.flutter.plugin( 3539): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
{code}
It seems Java is trying to cast assign a {{long}} value as in {{int}} in the following function (lines 354 and 355):

private TokenDetails decodeTokenDetails(Map<String, Object> jsonMap) {
if (jsonMap == null) return null;
final TokenDetails o = new TokenDetails();
readValueFromJson(jsonMap, PlatformConstants.TxTokenDetails.token, v -> o.token = (String) v);
readValueFromJson(jsonMap, PlatformConstants.TxTokenDetails.expires, v -> o.expires = (int) v);
readValueFromJson(jsonMap, PlatformConstants.TxTokenDetails.issued, v -> o.issued = (int) v);
readValueFromJson(jsonMap, PlatformConstants.TxTokenDetails.capability, v -> o.capability = (String) v);
readValueFromJson(jsonMap, PlatformConstants.TxTokenDetails.clientId, v -> o.clientId = (String) v);

In my dart code, when I truncate the the values in {{issued}} and {{expires}} to be epoch in seconds instead of milliseconds the error goes away and ably connects successfully to the server. The token is renewed at the correct time as well.

Thanks

@ikurek
Copy link
Contributor

ikurek commented Mar 31, 2022

Thank you for the report, it seems that you already located the issue, I'll create a fix for it ASAP 🙂

@ikurek ikurek self-assigned this Mar 31, 2022
@ikurek ikurek added the bug Something isn't working. It's clear that this does need to be fixed. label Mar 31, 2022
@ikurek
Copy link
Contributor

ikurek commented Mar 31, 2022

@gabrielginter I have suggested a fix for this in #358. I think it should resolve the problem entirely, but if it's possible, I would appreciate a confirmation from your side 🙂

@gabrielginter
Copy link
Author

@ikurek I'll try to test it by pointing my pubspec to your code as soon I get the chance.

By purely looking at the commit I think the fix will work just fine, as you mentioned, internally io.ably.lib.rest.Auth.TokenDetails is already using long so there should be no issues.

Thanks for the quick fix, hopefully there is a new release soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working. It's clear that this does need to be fixed.
Development

Successfully merging a pull request may close this issue.

2 participants