RoboSats / robosats

A simple and private bitcoin exchange
https://learn.robosats.com
GNU Affero General Public License v3.0
738 stars 145 forks source link

Android/iOS app. Torified webview in react-native. #42

Closed Reckless-Satoshi closed 2 years ago

Reckless-Satoshi commented 2 years ago

It might be a low hanging fruit to create a tiny react-native webview app wrapping the current webapp. It might also be possible to use react-native-tor https://github.com/Sifir-io/react-native-tor, so the user does not need TOR Browser nor Orbot, but only the .apk.

This could be a good start towards something more versatile and smartphone friendly.

Reckless-Satoshi commented 2 years ago

Currently under development: https://github.com/Reckless-Satoshi/robosats/pull/170

Encountering issues that require help from more experienced developers:

Special thanks and compensation (Sats) available to anyone that can help the embedded Android app work well with native tor.

For JS injection and message handling we could learn of what Hampus did for WebLN in Webview apps. https://github.com/hsjoberg/react-native-webln maybe even build a wrapper package "react-native-torified-webview" that can be helpful for other projects and be better maintained.

Other things that must be done for a functional and useful Android App:

Reckless-Satoshi commented 2 years ago

Bumping total rewards on this task x2 (from 1M to 2M Sats)

KoalaSat commented 2 years ago

Hello @Reckless-Satoshi ,I think I can start with fixing react-router

Reckless-Satoshi commented 2 years ago

Hello @Reckless-Satoshi ,I think I can start with fixing react-router

Assigned you. This task is pretty hard for a newbie like me. Ongoing work with everything needed to build/develop the Android App is in this branch https://github.com/Reckless-Satoshi/robosats/tree/android-webview-app-ts check /mobile/setup.md for my findings on how to setup a react-native development environment and build the webview app.

Will leave here some hints that @hsjoberg left on the Blixt wallet group to achieve a torified webview:

I don't know how react-native-webview does it, but in general, react-native uses OkHttp to handle web traffic. It's used by for example the Javascript fetch function.

So OkHttp has to be re-configured to run via the SOCKS proxy. Here is some code that I started working on before I ran into other things

 class CustomNetworkModule implements OkHttpClientFactory {
   public OkHttpClient createNewNetworkModuleClient() {
     return new OkHttpClient.Builder()
             .cookieJar(new ReactCookieJarContainer())
             .setProxy$okhttp(new Proxy(null, null))
             .build();
   }
 }

This would be done in MainActivity.java for the application in question, not in a react-native-torified-webview lib

I never got it to work properly but you should be able to set a new client with:

com.facebook.react.modules.network.OkHttpClientProvider.setOkHttpClientFactory(); ...or something like that.

I'm not sure the JS injection path you are taking about on GitHub is feasible because of security policies and whatnot that browsers often have. But yes I've had that thought too.

I think the proper fix has to be to take control over the traffic and make sure it's proxied via SOCKS.

OkHttp is used in react-native. But. Actually it's probably not true for the react-native-webview, because it makes use of the Android WebView component

KoalaSat commented 2 years ago

@Reckless-Satoshi I think the main point on loading the local bundle is based on this line https://github.com/Reckless-Satoshi/robosats/pull/170/files#diff-c92a46984ef937c6501f1fa10feebdf928270c5f2fbd7fe7b24aee34812a9c2cR24, but the URL doesn't seem to exist anymore 😅 Any other suggestion? do you think should I just start from scratch?

Reckless-Satoshi commented 2 years ago

@Reckless-Satoshi I think the main point on loading the local bundle is based on this line https://github.com/Reckless-Satoshi/robosats/pull/170/files#diff-c92a46984ef937c6501f1fa10feebdf928270c5f2fbd7fe7b24aee34812a9c2cR24

I think this URL only was interesting because it was a general solution that would work in both Android and iOS. I managed to access the content on that URL exported as PDF here

KoalaSat commented 2 years ago

I have been working on this and I would like to share what I have learned so far and expose the actual situation I'm facing. I explored 2 different ways for doing this, each of them with their own issues, so I'll try to expose them here the best I can:

Loading the JS through Tor

I have been trying to make this work, but looks like webview doesn't really likes to deal with directly Tor, there is still the possibility to overwrite the Http2 calls but as far I have seen, there is no clear solution for that.

The JS load is something unrelated to the Tor API requests, so far it's working fine and I found way less problems than expected.

That lead us to 2 posibilities:

Load the JS from clearnet, call back-end through Tor

This was the original approach. It actually works pretty well because I designed the Tor calls on a way it's independent from the JS source location.

PROS

Build the JS inside the App

Webpack can be configured to deploy the front-end to the /mobile folder so the JS code will stay inside of the App, that will help to create specific conditions on the code to deal better with the special cases on Android.

PROS


Let's have a talk! I would love to hear different oppinions

Reckless-Satoshi commented 2 years ago

Thanks a lot for this update and the work you are putting into getting the Android app done!

Build the JS inside the App

CONS

  • Android will have his own controlled versions and releases, which lead us to be careful on the back-end and be retro-compatible and news featres won't appear automatically.

  • The specific configuration and implementations for Android are there as part of the main JS build

  • Working locally has his own issues on WebView

I believe bundling the react.JS app inside the Android app is the way to go.

The fact that backend and Android app will have to be versioned and updated at the same time is already contemplated https://github.com/Reckless-Satoshi/robosats/issues/241 . We will hardcode the version of the react.JS app, and fetch from /api/info the version of the backend. If there is a mistmatch on major+minor (x.x.0), a Dialog will show with an explanation on how to keep RoboSats android client updated. We can release small patches (-.-.x) that won't trigger the Update dialog (can be done whenever the Backend API is still fully compatible).

What are the issues this approach has with the WebView? I was able to bundle the main.js with the app.apk, it worked except for the react-router (the bottom bar was there, but the UserGenPage did not render. What I did was to place main.js into /mobile/html/Web.bundle/frontend/main.js and in App.tsx set source={{ html: htmlPath }} on <WebView/>

KoalaSat commented 2 years ago

For the record and answering @Reckless-Satoshi I'll leave here the issues I'm working on:

Cookies not available on local files

https://github.com/react-native-webview/react-native-webview/issues/2643#issuecomment-1250315133 It seems to be an issue on react-native-webview. When loading the build using Web.bundle, cookies are not available.

Working on

Using https://github.com/Reckless-Satoshi/robosats/pull/247/files#diff-167fc1803bf1acdbd0d427666ac0f21ada0aecdae06acca7029fc8495c49fa7eR3 we can send and obtain the cookies information from Android's local storage.

MUI Avatar not loading

https://github.com/Reckless-Satoshi/robosats/pull/247/files#diff-d051bd00dd2196f76966e30471e3d9d9644b5026ef8d30e7f0b1bbe6113eebe9R206 while the avatar image loads properly everywhere, when loading it with MUI Avatar doesn't work, so it doesn't triggers and set avatarLoaded to true. It also fails even loading the JS from https://robosats.onion.moe .

Working on

Stop using MUI Avatar and use https://github.com/Reckless-Satoshi/robosats/blob/1771b4d18a4826f5aa36ece7953aeaa7d80138e2/frontend/src/components/Robots/RobotAvatar/index.tsx, which seems to work. We can do a fetch to the avatar URL and set avatarLoaded to true once the request succeeds.

External sources

https://github.com/Reckless-Satoshi/robosats/pull/247/files#diff-a3ab21f543adced6d1570ef4288a66c0f02a7f1d606265b398a5c8bc5ec97f76R83 Some external sources requires dynamic calculations so I have to find a way to obtain this throught Tor

Working on

Didn't tried yet but probably with Tor we can just obtain the Blob.

Fetching internal files

Related with previous issue, as an example the i18n json files, which also requires a specific configuration to deal with the i18n library. Looks like the fetch action does not allow access to urls with files:// protocol

Fetch API cannot load file:///android_asset/my_wasm_file.wasm. URL scheme "file" is not supported.

https://github.com/react-native-webview/react-native-webview/issues/1560

Working on

So far the only case is i18n so I'm considering configuring webpack to bundle the json files only for the android build.

react-native-tor does not implement PUT

😅 https://github.com/Sifir-io/react-native-tor/blob/ed9dc6f1c474c3aa1ae7512f7437343159a46480/android/src/main/java/com/reactnativetor/TorBridgeRequest.kt#L69

Working on

This is complicated, I can only figure out 2 solutions:

KoalaSat commented 2 years ago

I also found this https://github.com/Sifir-io/react-native-tor/issues/30#issuecomment-891060913, which is true for my local, I just thought it was related to my connection.

Reckless-Satoshi commented 2 years ago

Excellent work! This "torification" is certainly proving to be way more challenging than expected (... and the expectation was for it to be very hard).

This is complicated, I can only figure out 2 solutions:

* Check and create a PR for react-native-tor to include PUT actions
* Change our only PUT request to POST (I don't like this one)

Ha! We can simply open an issue in react-native-tor repo and do nothing to fix it for the moment: Android app users won't be able to disable Stealth Invoices, not a big deal. However, we wanted to use more PUT on API v1, it's good to know this is a limitation.

I also found this Sifir-io/react-native-tor#30 (comment), which is true for my local, I just thought it was related to my connection.

Nasty, it reads as if BlueWallet devs gave up on react-native-tor ?

The MUI Avatar component onLoad is basically a plain html tag <img onLoad={}/> it should be very generic so it is weird it is not working. In any case, it seems like no image is going to load and all of them will have to be fetch via TOR request as a blob as you suggested....

As for the cookies issues I see you have some clue leading to possible solutions, awesome! :rocket:

KoalaSat commented 2 years ago

https://github.com/Sifir-io/react-native-tor/issues/51

Reckless-Satoshi commented 2 years ago

Closing this issue. Current steps towards Android torified app in https://github.com/Reckless-Satoshi/robosats/issues/258 and #257

5eeman commented 1 year ago

@Reckless-Satoshi I've found solution for Fetching internal files described above. I've had same problem with getting .wasm files. In short - use XMLHttpRequest instead of fetch. Full solution described here.