Remove webcrypto polyfill, as it's no longer needed
parent
4eb8bc1249
commit
810fcf9910
31
README.md
31
README.md
|
@ -10,11 +10,6 @@ Uses:
|
||||||
- [MobX](https://mobx.js.org/README.html)
|
- [MobX](https://mobx.js.org/README.html)
|
||||||
- [Async Storage](https://github.com/react-native-async-storage/async-storage)
|
- [Async Storage](https://github.com/react-native-async-storage/async-storage)
|
||||||
|
|
||||||
## TODOs
|
|
||||||
|
|
||||||
- Handle the "unauthed" state better than changing route definitions
|
|
||||||
- Currently it's possible to get a 404 if the auth state changes
|
|
||||||
|
|
||||||
## Build instructions
|
## Build instructions
|
||||||
|
|
||||||
- Setup your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup).
|
- Setup your environment [using the react native instructions](https://reactnative.dev/docs/environment-setup).
|
||||||
|
@ -37,34 +32,8 @@ Uses:
|
||||||
|
|
||||||
## Various notes
|
## Various notes
|
||||||
|
|
||||||
### Env vars
|
|
||||||
|
|
||||||
Set using the `.env` file or using bash.
|
|
||||||
|
|
||||||
```
|
|
||||||
REACT_APP_AUTH_LOBBY = 'http://localhost:3001'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build behaviors
|
|
||||||
|
|
||||||
The `metro.config.js` file rewrites a couple of imports. This is partly to work around missing features in Metro, and partly to patch the bundle. Affected imports include:
|
|
||||||
|
|
||||||
- ucans
|
|
||||||
- one-webcrypto
|
|
||||||
|
|
||||||
### Cryptography
|
|
||||||
|
|
||||||
For native builds, we must provide a polyfill of `webcrypto`. We use a custom native module AppSecureRandom (based on [react-native-securerandom](https://github.com/robhogan/react-native-securerandom)) for the CRNG and [msrcrypto](https://github.com/microsoft/MSR-JavaScript-Crypto) for the cryptography.
|
|
||||||
|
|
||||||
**NOTE** Keys are not currently stored securely.
|
|
||||||
|
|
||||||
### Polyfills
|
### Polyfills
|
||||||
|
|
||||||
`./platform/polyfills.*.ts` adds polyfills to the environment. Currently this includes:
|
`./platform/polyfills.*.ts` adds polyfills to the environment. Currently this includes:
|
||||||
|
|
||||||
- webcrypto
|
|
||||||
- TextEncoder / TextDecoder
|
- TextEncoder / TextDecoder
|
||||||
|
|
||||||
### Auth flow
|
|
||||||
|
|
||||||
The auth flow is based on a browser app which is specified by the `REACT_APP_AUTH_LOBBY` env var. The app redirects to that location with the UCAN request, and then waits for a redirect back. In the native platforms with proper support, it will do this using an in-app browser. In native without in-app browser, or in the Web platform, it will handle this with redirects. The ucan is extracted from the hash fragment of the "return url" which is provided either by the in-app browser in response or detected during initial setup in the case of redirects.
|
|
|
@ -1,28 +0,0 @@
|
||||||
package xyz.blueskyweb.app;
|
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
|
||||||
import com.facebook.react.bridge.Promise;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import android.util.Base64;
|
|
||||||
|
|
||||||
public class AppSecureRandomModule extends ReactContextBaseJavaModule {
|
|
||||||
public AppSecureRandomModule(ReactApplicationContext context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ReactMethod
|
|
||||||
public void generateSecureRandomAsBase64(int length, Promise promise) {
|
|
||||||
SecureRandom secureRandom = new SecureRandom();
|
|
||||||
byte[] buffer = new byte[length];
|
|
||||||
secureRandom.nextBytes(buffer);
|
|
||||||
promise.resolve(Base64.encodeToString(buffer, Base64.NO_WRAP));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "AppSecureRandomModule";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package xyz.blueskyweb.app;
|
|
||||||
|
|
||||||
import com.facebook.react.ReactPackage;
|
|
||||||
import com.facebook.react.bridge.NativeModule;
|
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
|
||||||
import com.facebook.react.uimanager.ViewManager;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AppSecureRandomPackage implements ReactPackage {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
||||||
List<NativeModule> modules = new ArrayList<>();
|
|
||||||
modules.add(new AppSecureRandomModule(reactContext));
|
|
||||||
return modules;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -28,7 +28,6 @@ public class MainApplication extends Application implements ReactApplication {
|
||||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||||
// packages.add(new MyReactNativePackage());
|
// packages.add(new MyReactNativePackage());
|
||||||
packages.add(new AppSecureRandomPackage());
|
|
||||||
return packages;
|
return packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
#import <React/RCTBridgeModule.h>
|
|
||||||
|
|
||||||
@interface AppSecureRandomModule : NSObject <RCTBridgeModule>
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,27 +0,0 @@
|
||||||
#import "AppSecureRandomModule.h"
|
|
||||||
|
|
||||||
@implementation AppSecureRandomModule
|
|
||||||
|
|
||||||
RCT_EXPORT_MODULE();
|
|
||||||
|
|
||||||
+ (BOOL)requiresMainQueueSetup
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
RCT_REMAP_METHOD(generateSecureRandomAsBase64,
|
|
||||||
withLength:(int)length
|
|
||||||
resolver:(RCTPromiseResolveBlock)resolve
|
|
||||||
rejecter:(RCTPromiseRejectBlock)reject)
|
|
||||||
{
|
|
||||||
NSMutableData* bytes = [NSMutableData dataWithLength:length];
|
|
||||||
int result = SecRandomCopyBytes(kSecRandomDefault,length, [bytes mutableBytes]);
|
|
||||||
if (result == errSecSuccess) {
|
|
||||||
resolve([bytes base64EncodedStringWithOptions:0]);
|
|
||||||
} else {
|
|
||||||
NSError *error = [NSError errorWithDomain:@"RNSecureRandom" code:result userInfo: nil];
|
|
||||||
reject(@"randombytes_error", @"Error generating random bytes", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -14,8 +14,6 @@
|
||||||
5CEAE7B7A55582F96F1D5952 /* libPods-app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FCB672808307A6013805A3FE /* libPods-app.a */; };
|
5CEAE7B7A55582F96F1D5952 /* libPods-app.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FCB672808307A6013805A3FE /* libPods-app.a */; };
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||||
E4BBD590292C1F5200296224 /* app.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = E4437C9E28581FA7006DA9E7 /* app.entitlements */; };
|
E4BBD590292C1F5200296224 /* app.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = E4437C9E28581FA7006DA9E7 /* app.entitlements */; };
|
||||||
E4BD704B285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */ = {isa = PBXBuildFile; fileRef = E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */; };
|
|
||||||
E4BD704C285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */ = {isa = PBXBuildFile; fileRef = E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */; };
|
|
||||||
FEB90D21557517F9279AECA4 /* libPods-app-appTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BAD3BC60FA05CF2D4F6F9BA2 /* libPods-app-appTests.a */; };
|
FEB90D21557517F9279AECA4 /* libPods-app-appTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BAD3BC60FA05CF2D4F6F9BA2 /* libPods-app-appTests.a */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
@ -45,8 +43,6 @@
|
||||||
970005155A83960D1D13380F /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = "<group>"; };
|
970005155A83960D1D13380F /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
BAD3BC60FA05CF2D4F6F9BA2 /* libPods-app-appTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app-appTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
BAD3BC60FA05CF2D4F6F9BA2 /* libPods-app-appTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app-appTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
E4437C9E28581FA7006DA9E7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = app.entitlements; path = app/app.entitlements; sourceTree = "<group>"; };
|
E4437C9E28581FA7006DA9E7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = app.entitlements; path = app/app.entitlements; sourceTree = "<group>"; };
|
||||||
E4BD7049285AD54000A8FED9 /* AppSecureRandomModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppSecureRandomModule.h; sourceTree = "<group>"; };
|
|
||||||
E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppSecureRandomModule.m; sourceTree = "<group>"; };
|
|
||||||
ED22CAA45207BC18E75DB44B /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = "<group>"; };
|
ED22CAA45207BC18E75DB44B /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||||
FCB672808307A6013805A3FE /* libPods-app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
FCB672808307A6013805A3FE /* libPods-app.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-app.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -99,8 +95,6 @@
|
||||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||||
13B07FB71A68108700A75B9A /* main.m */,
|
13B07FB71A68108700A75B9A /* main.m */,
|
||||||
E4BD7049285AD54000A8FED9 /* AppSecureRandomModule.h */,
|
|
||||||
E4BD704A285AD57E00A8FED9 /* AppSecureRandomModule.m */,
|
|
||||||
);
|
);
|
||||||
name = app;
|
name = app;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -377,7 +371,6 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
00E356F31AD99517003FC87E /* appTests.m in Sources */,
|
00E356F31AD99517003FC87E /* appTests.m in Sources */,
|
||||||
E4BD704C285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -386,7 +379,6 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||||
E4BD704B285AD57E00A8FED9 /* AppSecureRandomModule.m in Sources */,
|
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -8,55 +8,6 @@ const metroResolver = require('metro-resolver')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
resolver: {
|
|
||||||
resolveRequest: (context, moduleName, platform) => {
|
|
||||||
// HACK
|
|
||||||
// metro doesn't support the "exports" directive in package.json
|
|
||||||
// so we have to manually fix some imports
|
|
||||||
// see https://github.com/facebook/metro/issues/670
|
|
||||||
// -prf
|
|
||||||
if (moduleName.startsWith('ucans')) {
|
|
||||||
const subpath = moduleName.split('/').slice(1)
|
|
||||||
if (subpath.length === 0) {
|
|
||||||
subpath.push('index.js')
|
|
||||||
} else {
|
|
||||||
subpath[subpath.length - 1] = `${subpath[subpath.length - 1]}.js`
|
|
||||||
}
|
|
||||||
const filePath = path.join(
|
|
||||||
context.projectRoot,
|
|
||||||
'node_modules',
|
|
||||||
'ucans',
|
|
||||||
'dist',
|
|
||||||
'cjs',
|
|
||||||
...subpath,
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
type: 'sourceFile',
|
|
||||||
filePath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// HACK
|
|
||||||
// this module has the same problem with the "exports" module
|
|
||||||
// but also we need modules to use our version of webcrypto
|
|
||||||
// so here we're routing to a module we define
|
|
||||||
// -prf
|
|
||||||
if (moduleName === 'one-webcrypto') {
|
|
||||||
return {
|
|
||||||
type: 'sourceFile',
|
|
||||||
filePath: path.join(
|
|
||||||
context.projectRoot,
|
|
||||||
'src',
|
|
||||||
'platform',
|
|
||||||
'polyfills.native.ts',
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// default resolve
|
|
||||||
delete context.resolveRequest
|
|
||||||
return metroResolver.resolve(context, moduleName, platform)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
transformer: {
|
transformer: {
|
||||||
getTransformOptions: async () => ({
|
getTransformOptions: async () => ({
|
||||||
transform: {
|
transform: {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {RootSiblingParent} from 'react-native-root-siblings'
|
||||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
||||||
import SplashScreen from 'react-native-splash-screen'
|
import SplashScreen from 'react-native-splash-screen'
|
||||||
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
||||||
import {whenWebCrypto} from './platform/polyfills.native'
|
|
||||||
import * as view from './view/index'
|
import * as view from './view/index'
|
||||||
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
||||||
import {MobileShell} from './view/shell/mobile'
|
import {MobileShell} from './view/shell/mobile'
|
||||||
|
@ -17,23 +16,19 @@ function App() {
|
||||||
|
|
||||||
// init
|
// init
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
whenWebCrypto
|
view.setup()
|
||||||
.then(() => {
|
setupState().then(store => {
|
||||||
view.setup()
|
setRootStore(store)
|
||||||
return setupState()
|
SplashScreen.hide()
|
||||||
})
|
Linking.getInitialURL().then((url: string | null) => {
|
||||||
.then(store => {
|
if (url) {
|
||||||
setRootStore(store)
|
|
||||||
SplashScreen.hide()
|
|
||||||
Linking.getInitialURL().then((url: string | null) => {
|
|
||||||
if (url) {
|
|
||||||
store.nav.handleLink(url)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Linking.addEventListener('url', ({url}) => {
|
|
||||||
store.nav.handleLink(url)
|
store.nav.handleLink(url)
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
|
Linking.addEventListener('url', ({url}) => {
|
||||||
|
store.nav.handleLink(url)
|
||||||
|
})
|
||||||
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// show nothing prior to init
|
// show nothing prior to init
|
||||||
|
|
|
@ -1,30 +1 @@
|
||||||
import {NativeModules} from 'react-native'
|
|
||||||
const {AppSecureRandomModule} = NativeModules
|
|
||||||
import {toByteArray} from 'base64-js'
|
|
||||||
// @ts-ignore we dont have types for this -prf
|
|
||||||
import crypto from '../third-party/msrcrypto'
|
|
||||||
import '@zxing/text-encoding' // TextEncoder / TextDecoder
|
import '@zxing/text-encoding' // TextEncoder / TextDecoder
|
||||||
|
|
||||||
async function generateSecureRandom(bytes: number) {
|
|
||||||
return toByteArray(
|
|
||||||
await AppSecureRandomModule.generateSecureRandomAsBase64(bytes),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const whenWebCrypto = new Promise(async (resolve, reject) => {
|
|
||||||
try {
|
|
||||||
const bytes = await generateSecureRandom(48)
|
|
||||||
crypto.initPrng(Array.from(bytes))
|
|
||||||
|
|
||||||
// @ts-ignore global.window exists -prf
|
|
||||||
if (!global.window.crypto) {
|
|
||||||
// @ts-ignore global.window exists -prf
|
|
||||||
global.window.crypto = crypto
|
|
||||||
}
|
|
||||||
resolve(true)
|
|
||||||
} catch (e: any) {
|
|
||||||
reject(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const webcrypto = crypto
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue