alik0211 / mtproto-core

Telegram API JS (MTProto) client library for Node.js and browser
https://mtproto-core.js.org
GNU General Public License v3.0
624 stars 111 forks source link

crypto and localstorage error in react native #96

Open destimon opened 3 years ago

destimon commented 3 years ago

Currently trying to use mtproto-core in react-native project.

Have next issues:

During call method from the instance - getting undefined localStorage

[03:34:24] [Unhandled promise rejection: TypeError: undefined is not an object (evaluating 'this.localStorage.getItem')]
- node_modules\@mtproto\core\src\storage\index.js:49:35 in Storage#get
- node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
- node_modules\regenerator-runtime\runtime.js:293:29 in invoke
- node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
- node_modules\regenerator-runtime\runtime.js:154:27 in invoke
- node_modules\regenerator-runtime\runtime.js:189:16 in PromiseImpl$argument_0
- node_modules\promise\setimmediate\core.js:45:6 in tryCallTwo
- node_modules\promise\setimmediate\core.js:200:22 in doResolve
- node_modules\promise\setimmediate\core.js:66:11 in Promise
- node_modules\regenerator-runtime\runtime.js:188:15 in callInvokeWithMethodAndArg
- node_modules\regenerator-runtime\runtime.js:211:38 in enqueue
- node_modules\regenerator-runtime\runtime.js:238:8 in exports.async
* http://192.168.0.105:19001/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&minify=false&hot=false:171436:41 in get
- node_modules\@mtproto\core\src\index.js:75:40 in MTProto#call
- node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
- node_modules\regenerator-runtime\runtime.js:293:29 in invoke
- node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
- node_modules\regenerator-runtime\runtime.js:154:27 in invoke
- node_modules\regenerator-runtime\runtime.js:189:16 in PromiseImpl$argument_0
- node_modules\promise\setimmediate\core.js:45:6 in tryCallTwo
- node_modules\promise\setimmediate\core.js:200:22 in doResolve
- node_modules\promise\setimmediate\core.js:66:11 in Promise
- node_modules\regenerator-runtime\runtime.js:188:15 in callInvokeWithMethodAndArg
- node_modules\regenerator-runtime\runtime.js:211:38 in enqueue
- node_modules\regenerator-runtime\runtime.js:238:8 in exports.async
* http://192.168.0.105:19001/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&minify=false&hot=false:141073:41 in call
- node_modules\react-native\Libraries\Components\Touchable\TouchableNativeFeedback.js:169:10 in Pressability$argument_0.onPress
- node_modules\react-native\Libraries\Pressability\Pressability.js:655:17 in _performTransitionSideEffects
- node_modules\react-native\Libraries\Pressability\Pressability.js:589:6 in _receiveSignal
- node_modules\react-native\Libraries\Pressability\Pressability.js:499:8 in responderEventHandlers.onResponderRelease
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:286:4 in invokeGuardedCallbackImpl
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:497:2 in invokeGuardedCallback
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:521:2 in invokeGuardedCallbackAndCatchFirstError
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:683:41 in executeDispatch
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:707:19 in executeDispatchesInOrder
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:872:28 in executeDispatchesAndRelease
* [native code]:null in forEach
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:851:4 in forEachAccumulated
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:897:20 in runEventsInBatch
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:1069:18 in runExtractedPluginEventsInBatch
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:2835:35 in batchedUpdates$argument_0
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:20569:13 in batchedUpdates$1
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:2731:29 in batchedUpdates
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:2834:16 in _receiveRootNodeIDEvent
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:2911:27 in receiveTouches
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:425:19 in __callFunction
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:6 in __guard$argument_0
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:373:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:111:4 in callFunctionReturnFlushedQueue
* [native code]:null in callFunctionReturnFlushedQueue

Any call method causes that error. Obviously, localStorage property, as i checked, is undefined in mtproto instance after initializating.

const confirmPhone = useCallback(() => {
  const api_id = [API_ID];
  const api_hash = [API_HASH];

  const mtproto = new MTProto({
    api_id,
    api_hash,
  });

  mtproto.call("auth.sendCode", {
    phone_number: "9996621234",
    settings: {
      _: "codeSettings",
    },
  });
}, []);

I've tried to replace default storage with custom storage:

const confirmPhone = useCallback(() => {
  const api_id = [API_ID];
  const api_hash = [API_HASH];

  const mtproto = new MTProto({
    api_id,
    api_hash,
    customLocalStorage: tempLocalStorage, // Custom storage from @mtproto/core/src/storage/temp
  });

  mtproto.call("auth.sendCode", {
    phone_number: "9996621234",
    settings: {
      _: "codeSettings",
    },
  });
}, []);

and now getting the next error:

[03:44:27] [Unhandled promise rejection: ReferenceError: Can't find variable: crypto]
- node_modules\@mtproto\core\src\utils\common\random\index.browser.js:1:0 in getRandomBytes
- node_modules\@mtproto\core\src\rpc\index.js:723:18 in RPC#updateSession
- node_modules\@mtproto\core\src\rpc\index.js:34:19 in RPC#constructor
- node_modules\@mtproto\core\src\index.js:129:18 in MTProto#createRPC
* http://192.168.0.105:19001/node_modules%5Cexpo%5CAppEntry.bundle?platform=android&dev=true&minify=false&hot=false:141104:30 in call$
- node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
- node_modules\regenerator-runtime\runtime.js:293:29 in invoke
- node_modules\regenerator-runtime\runtime.js:63:36 in tryCatch
- node_modules\regenerator-runtime\runtime.js:154:27 in invoke
- node_modules\regenerator-runtime\runtime.js:164:18 in PromiseImpl.resolve.then$argument_0
- node_modules\promise\setimmediate\core.js:37:13 in tryCallOne
- node_modules\promise\setimmediate\core.js:123:24 in setImmediate$argument_0
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:135:14 in _callTimer
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:183:16 in _callImmediatesPass
- node_modules\react-native\Libraries\Core\Timers\JSTimers.js:446:30 in callImmediates
* [native code]:null in callImmediates
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:396:6 in __callImmediates
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:144:6 in __guard$argument_0
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:373:10 in __guard
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:143:4 in flushedQueue
* [native code]:null in flushedQueue
* [native code]:null in callFunctionReturnFlushedQueue

Seems like it can't support Node.js, but in simple Node.js project(w/o any frameworks) everything works fine.

$ node -v
v14.10.1

Will be grateful for any help šŸ™šŸ».

alik0211 commented 3 years ago
  1. By storage: you need to write your own customLocalStorage.
  2. And probably, now cryptographic functions are not compatible with react-native. I will try to fix this in the next release

You can follow future releases in the official telegram channel - https://t.me/mtproto_core

happymaskterriblefate commented 3 years ago

I've spent a bunch of time attempting to get the correct crypto APIs to be available in react-native, without success.

From what I can tell, the APIs required are:

I've found alternatives to these APIs in various packages, but ultimately run into either compatibility problems or an API that is async when MTProto expects it to be sync (getRandomBytes for example).

Do you have a suggestion for an alternative library that would work in react-native? If not, is there anything I can do to help get react-native support? Thanks!

happymaskterriblefate commented 3 years ago

I had just about given up, but then tried the react-native-webview-crypto package.

Here's what worked for me:

App.js

import React, { useEffect } from "react";
import WebviewCrypto from "react-native-webview-crypto";
import { TextEncoder, TextDecoder } from "web-encoding";
import "react-native-get-random-values";
import { View } from "react-native";
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
import MTProto from "@mtproto/core/envs/browser";

polyfillGlobal("TextEncoder", () => TextEncoder);
polyfillGlobal("TextDecoder", () => TextDecoder);

// Dummy storage to get things working
class CustomStorage {
  constructor() {
    this.data = {};
  }

  set(key, value) {
    this.data[key] = value;
    return Promise.resolve();
  }

  get(key) {
    return Promise.resolve(this.data[key]);
  }
}

const App = () => {
  useEffect(() => {
    // REPLACE WITH YOUR VALUES
    const api_id = "xxxxxxx";
    const api_hash = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    const mtproto = new MTProto({
      api_id,
      api_hash,

      storageOptions: {
        instance: new CustomStorage(),
      },
    });

    // 2. Print the user country code
    mtproto.call("help.getNearestDc").then(result => {
      console.log("Nearest DC:", result.country);
    });
  }, []);

  return (
    <View>
      <WebviewCrypto />
    </View>
  );
};

export default App;

You'll need react-native-webview-crypto, web-encoding, react-native-get-random-values, as well as the polyfillGlobal helper built into react-native. Then load the browser version of MTProto located at @mtproto/core/envs/browser.

image

I hope this saves someone time in the future. If I run into any other issues, I'll followup in this thread.

happymaskterriblefate commented 3 years ago

Added a PR here: https://github.com/alik0211/mtproto-core/pull/187