Mobile WebView

Integrating Aerosync Web SDK in Mobile Apps via WebView

Introduction

This guide is intended for developers who are integrating the Aerosync Web NPM SDK into mobile applications by loading a web application inside a WebView. If your mobile app is built using Capacitor, React Native, or native platforms like Android or iOS and renders a web app that includes the Aerosync widget, this document will walk you through the correct approach to installing the SDK within the web app, handling OAuth-based bank authentication securely, and ensuring a smooth user experience by properly managing external URL redirects.

👍

Fetch SDK token GET /aggregatorCredentials

The token for the Aerosync SDK will be returned in the response to GET /aggregatorCredentials. Read the Aerosync guide for details.

1. Installation & Setup

Install the Aerosync Web SDK inside your web application, not the mobile app directly.

npm i aerosync-web-sdk

Follow the guide Aerosync Integration Guide to integrate the Aerosync widget into your Web app. https://dev.aero.inc/docs/web-npm-sdk#/

Make sure to add the deeplink in the widget configuration. This will help redirect the user back to the main app after authentication.

2. Handling OAuth Bank Authentication

❗️

Important Note on OAuth in WebViews

OAuth connections must not be launched inside WebViews. Instead, they should be opened in the system browser or in-app browser. Aerosync’s SDK automatically uses window.open to launch OAuth URLs in the system browser. This is by design, because: WebViews are considered insecure containers by many banks. OAuth providers like Chase explicitly block WebView-based authentication due to lack of cookie persistence, limited security context, and the inability to verify domain authenticity.

Why System Browser or InAppBrowser? System browsers (Safari/Chrome) are secure, trusted, and allow the user to complete bank authentication reliably. InAppBrowsers (e.g., Capacitor or React Native plugins) are sandboxed but offer more control than plain WebViews.

2.1 Open External URLs in System Browser

Update your WebView configuration to ensure external URLs (triggered by window.open) are opened in the system browser, not inside the WebView.

Example: React Native WebView

<WebView
  source={{ uri: "https://your-web-app.com" }}
  onOpenWindow={(syntheticEvent) => {
    const { nativeEvent } = syntheticEvent;
    const { targetUrl } = nativeEvent;
    try {
      Linking.openURL(targetUrl); // Opens in system browser
    } catch (e) {
      console.error("Unable to open URL:", e);
    }
  }}
/>

Example: Native iOS (WKWebView) Use the WKUIDelegate method to handle new window requests:

func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    if let url = navigationAction.request.url {
        UIApplication.shared.open(url)
    }
    return nil
}

Example: Native Android (WebView) Override shouldOverrideUrlLoading:

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith("http")) {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        context.startActivity(intent);
        return true;
    }
    return false;
}

2.2 Recommended: Manual OAuth Handling with Aerosync SDK

By default, the Aerosync widget opens OAuth URLs in the system browser using window.open. This behavior is designed to meet security requirements enforced by many financial institutions. However, if you need more control over how the OAuth flow is launched—such as using an in-app browser or implementing a custom redirect flow—you can enable manual handling by setting the handleOAuthManually flag to true in the widget configuration.

When handleOAuthManually is enabled, the widget will not trigger window.open. Instead, it will emit an event through the onEvent callback, providing the OAuth URL in the payload. This allows your app to launch the authentication flow in a way that best fits your architecture and user experience.

Aerosync Widget Configuration Example

const widgetRef = openWidget({
  id: "widget",
  iframeTitle: "Connect",
  environment: "sandbox", // or "production"
  token,
  handleOAuthManually: true,
  onEvent:(event) => {
    if (
      event?.payload?.pageTitle === "launchExternalWindow" &&
      event?.payload?.onLoadApi
    ) {
      const oauthUrl = event.payload.onLoadApi;

      // Send OAuth URL to host app (WebView)
      // Construct the message payload (customize as needed)      
      const message = JSON.stringify({
        type: "AEROSYNC_OAUTH_URL",
        url: oauthUrl,
      });

      // React Native WebView
      if (window.ReactNativeWebView?.postMessage) {
        window.ReactNativeWebView.postMessage(message);
      }
      // iOS WKWebView
      else if (window.webkit?.messageHandlers?.aerosync) {
        window.webkit.messageHandlers.aerosync.postMessage(JSON.parse(message));
      }
      // Android WebView
      else if (window.Android?.postMessage) {
        window.Android.postMessage(message);
      }
    }
  },
});

Setting handleOAuthManually: true gives you full control over when and how the OAuth authentication flow is launched. The widget will emit an event instead of opening a new browser window, allowing you to handle the flow in a way that suits your app.

Example OAuth Payload When the widget is ready to begin the OAuth flow, it will emit an event structured like this:

{
  type: "externalWindow",
  payload: {
    pageTitle: "launchExternalWindow",
    onLoadApi: "https://oauth.bank.com/..." // OAuth redirect URL
  }
}

Mobile Webview Examples

React Native

<WebView
  source={{ uri: "https://your-widget-host.com" }}
  onMessage={(event) => {
    const data = JSON.parse(event.nativeEvent.data);
    if (data.type === "AEROSYNC_OAUTH_URL") {
      Linking.openURL(data.url); // or InAppBrowser
    }
  }}
/>

iOS (Swift, WKWebView)

contentController.add(self, name: "aerosync")

func userContentController(
  _ userContentController: WKUserContentController,
  didReceive message: WKScriptMessage
) {
  if message.name == "aerosync",
     let body = message.body as? [String: Any],
     body["type"] as? String == "AEROSYNC_OAUTH_URL",
     let urlString = body["url"] as? String,
     let url = URL(string: urlString) {
       present(SFSafariViewController(url: url), animated: true)
  }
}

Android (Kotlin, WebView)

webView.addJavascriptInterface(WebAppInterface(), "Android")

inner class WebAppInterface {
  @JavascriptInterface
  fun postMessage(message: String) {
    val json = JSONObject(message)
    if (json.getString("type") == "AEROSYNC_OAUTH_URL") {
      val oauthUrl = json.getString("url")
      startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(oauthUrl)))
    }
  }
}