My company, CriticalBlue, provides a remote mobile app authentication service called . An Approov SDK is provided as a drop-in library to native iOS and Android app developers. Approov With many of our customers using or experimenting with React Native, I wanted to provide a convenient Javascript module which exposes the native Approov SDK functionality to React Native developers. Overall, creating my first React Native native module was surprisingly straightforward. All code for my initial proof of concept is available in a . github repository This story describes bridging to iOS native modules. For the same example targeting Android, see . First experiences with React Native: bridging an Android native module for app authentication React Native Bridge For React Native, Facebook provides a solid which goes over the basics, including how to set up an application and how the underlying UI components differ from the usual React web elements. getting started guide from React Made Native Easy Architecturally, there are two important threads running in each React Native application — one a main UI thread and the other running a Javascript VM. The two threads interact through a bridge whose communication is asynchronous, serialized, and batched, decoupling the two systems as much as possible. While the bulk of the React Native app is described in React and runs on the Javascript VM, the UI is rendered using the native platform’s UI elements, and actions which alter the app’s UI are passed as messages through the bridge from the VM to the app’s main UI thread. System functionality and libraries developed for the native device environment can be exposed to the Javascript VM using React Native’s native module interface and accessed through the React Native bridge. Approov Native SDK The Approov SDK is a drop-in native iOS or Android library. It interfaces with the cloud-based Approov authentication service which validates that the app is genuine, untampered, and not a bot. An app integrity token is returned from the authentication service, and that token is sent with each API call to ensure that the back-end API service is dealing with a known and genuine front-end request. The basic operation we will expose in our native module proof of concept is , an asynchronous operation in the native SDK. fetchApproovToken() Approov-protected API call Before each back-end API call requiring app authentication, the client app makes a fetch token request. If a fresh token is needed, the SDK makes a remote attestation request, and the attestation service cryptographically authenticates the app and responds with an app integrity token. The token has a short lifetime and is signed by a secret known only to the Approov service and the app’s back-end service. No secret is stored in the app, and in fact the app does not know whether the returned token is valid or not. The app simply adds the integrity token to the back-end API call, and the back-end server validates that the token has not expired and is properly signed before processing the request. Approov Demo Service Approov offers a which provides demonstration iOS and Android SDKs and a back-end service with two endpoints: downloadable demo , which provides a publicly accessible test point. https://demo-server.approovr.io/hello , which provides a random shape only if the request contains a valid integrity token. https://demo-server.approovr.io/shapes We’ll use the iOS SDK with the back-end service to demonstrate a simple React Native app using Approov. For the Android version of this example, see . First experiences with React Native: bridging an Android native module for app authentication Saying Hello I started my React Native project using create-react-native-app (CRNA). Follow the to setup your React Native environment and then: CRNA installation instructions $ create-react-native-app rndemo I will be adding native code to the app, so go ahead and eject now from create-react-native-app: $ cd rndemo$ yarn eject... Ejecting is permanent! Please be careful with your selection. ? How would you like to eject from create-react-native-app? React Native: I’d like a regular React Native project.We have a couple of questions to ask you about how you’d like to name your app:? What should your app appear as on a user’s home screen? RN Demo? What should your Android Studio and Xcode projects be called? rndemo Wrote to app.json, please update it manually in the future.Generating the iOS folder.Generating the Android folder.... Select a regular React Native project and name it as you wish. The iOS and Android projects are generated, and you will need and/or build environments installed. Native code will be added to our native iOS project later on. Xcode Android Studio We will experiment with a very simple proof of concept app that will use the demo server hello endpoint to validate our network connectivity. React Native implements the for networking. We combine the connection check, UI rendering, and styling all in the file: fetch API App.js The top-level App component is registered in the file. index.js The main view is rendered by a stateless view component which displays a choice of image and a status message: I am using iOS for these examples, but this works similarly on Android. Fire up an iOS simulator (you may need to launch Xcode for this). In the directory, launch the app: rndemo $ cd rndemo$ yarn run ios You should see a screen like this: Push the button and you should see a connected message if everything went okay: Test Hello This validates network communication between our React Native app and the demo server. To run on an actual iPhone, connect the iPhone to your Mac. In Xcode, open the React Native iOS project, select the iPhone as your target and run the application. To test for no connection, if running on the simulator, disable your Mac’s WiFi or networking and push the button. If running on a phone, set airplane mode and then push the button. Test Hello Test Hello The Approov Native Module The Approov demo package includes a README, the iOS and Android Approov demo libraries, sample clients, and app registration tools. Download the , and save the app registration token which is included in your download email. demo package The Approov SDK includes the native code we want to expose to React Native. It must be included in the iOS or Android native projects which were generated when we ejected the create-react-native-app. For iOS, this project is located at . Add the Approov SDK framework into the iOS project by following these from the . rndemo/ios instructions Approov docs The iOS Approov SDK currently does not support bitcode, so you should set to in your Xcode project settings. Build Settings -> Build Options -> Enable Bitcode No In iOS, a native module is an Objective-C class that implements the protocol. Our example will mix Objective-C and Swift to register and implement the bridge to native code. RCTBridgeModule We’ll start by creating the bridge class in Swift with a call which wraps the asynchronous native call and settles a promise when the token fetch completes. Approov fetchApproovToken() fetchApproovToken() In the call, the promise is settled by calling the resolve or the reject . Our implementation simply resolves with the fetched token if successful or a sentinel on failure. Both closure blocks are marked with attributes to indicate that they may complete after the call completes. modifiers are used to export the class and functions to the Objective-C runtime. fetchApproovToken() RCTPromiseResolveBlock RCTPromiseRejectBlock NO_TOKEN @escaping fetchApproovToken() @objc The Objective-C implementation connects the Objective-C exposed Swift class to the React Native bridge. The macro registers the class, and the macro exposes the call. RCT_EXTERN_MODULE() Approov RCT_EXTERN_METHOD() fetchApproovToken() Whenever you mix Swift and Objective-C in the same project, you need a bridging header to expose the Objective-C files to Swift. It must include the React Native bridge headers. On the Javascript side, the Approov native module will now be included in the imported from . In our implementation, returns a normal javascript promise: NativeModules react-native Approov.fetchApproovToken() Bridging the native and Javascript environments was a bit confusing when mixing Swift and Objective-C, but in the end it required only a small amount of code. Interceptors Many networking libraries, such as and tp, include the concept of an interceptor. Interceptors can be used to intercept network requests and responses and inject some additional processing. Axios OkHt When implementing natively with the Android SDK, most customers use interceptors to fetch an Approov token and add it to each API request’s headers, so we’ll want to fully implement this abstraction in our production module. For this simple example though, we’ll hardwire the interception in a call. fetchWithToken() In the method, when the native fetch token call completes, if the promise is resolved, we add the token to the input request headers and make a call with the augmented input request. When completed, the fetch returns a resolved promise holding the API server’s response. fetchWithToken() fetch() For convenience, we create an object from the object, adding a method which is actually the method, and then we export this as the module. Approov NativeModules.Approov fetch() fetchWithToken() Approov Getting Shapes Now we are ready to use the object for authentication. We add a method inside our which makes an call to authenticate and request a random shape value. Once the fetch completes, the component state updates, triggering a call which causes the to display an updated shape and status message. Approov getShape() App Approov.fetch(request) App render() ShapeView A button is added to the button bar to request new shapes. Get Shapes Everything looks good, but when we request a new shape, we see a failure with a 400 status code, suggesting there is a problem with the client request. Failure to fetch a shape The call fails because the integrity token added by Approov is invalid. Until our example app is properly registered with the Approov service, the call will always fail the authentication check. fetchWithToken() Command line registration tools are included in the demo download. To register the app, issue a registration request specifying the App’s IPA archive and the app registration token you saved from the demo download email. You can generate the IPA archive from the product inside Xcode. As a courtesy to other demo users, set your registration to expire after a few hours using the flag: rndemo.app -e $ cd <<approov-demo-package>>/registration-tools/Android/Mac/$ ./registration-a <<your-rndemo.ipa>>-t <registration-token>-e 2h Submitting data…Success: new app signature added to database. Once the app is registered and can be properly authenticated, pressing the button should return one of these shapes: Get Shapes Successfully fetching random shapes Man in the Middle Attacks The security of the communication channel is very important during API calls. If the channel is insecure, an API call could be intercepted and modified. An integrity token, although it has a short lifetime, could be observed in the insecure channel and used to make malicious API calls with impunity. Despite using HTTPS/TLS when making API requests, an attacker who controls both the network and the mobile device can easily setup a to steal and quickly reuse Approov tokens before they expire. Man in the Middle (MitM) attack To counter MitM attacks, mobile clients should use which checks that the certificate or public key presented by the back-end service is known specifically by the client app. Other certificates, though they might appear authentic, will be rejected by the client, and no API calls will be made. certificate or public key ‘pinning’ Implementing pinning in React Native is a bit complicated and will be described in a separate article and integrated into this example’s code repository. Going Further We’ve demonstrated a native module implementation in React Native with a hardwired interceptor successfully providing app authentication and API protection. A production quality native module implementation for React Native would generalize the interceptor functionality, add convenience configuration methods, and provide full MitM protection. For comparison, a similar already exists for and hybrid apps. Approov plugin library Cordova Ionic All code for this example is located on . github Thanks for reading! For more information on mobile API security, check out . www.approov.io I’d really appreciate it if you recommend this post (by clicking the 👏 button) so other people can find it.