Table of Contents

    Book an Appointment

    INTRODUCTION

    While working on a phased modernization for a large-scale SaaS financial platform, we encountered a complex user experience bottleneck. The client was gradually migrating their legacy enterprise application, originally written in Cordova, to a modern React Native architecture. During this transitional phase, users needed to have both apps installed on their mobile devices to access different subsets of features.

    We realized that forcing users to log in twice—once in the legacy app and again in the new React Native app—was causing immense friction. We needed a secure way to share authentication tokens between the two applications. If a user logged into the legacy Cordova app, the modern React Native app needed to detect those tokens, validate them, and seamlessly refresh the session without user intervention. Attempting to bridge two fundamentally different frameworks across iOS and Android security sandboxes presented unique challenges. This scenario inspired this article, providing a technical blueprint for engineering teams navigating similar cross-app authentication hurdles so they can avoid the security pitfalls we encountered.

    PROBLEM CONTEXT

    The core business requirement was simple: establish a Single Sign-On (SSO) style experience between two apps published by the same developer.

    When a user authenticates in the legacy Cordova application, the server issues an access_token and a refresh_token. These tokens must be securely stored in a shared context. When the user subsequently launches the modern React Native application, it must query this shared context. If valid tokens are found, the React Native app uses them to initialize the user session. If the access token is expired, it uses the refresh token to negotiate a new session.

    However, mobile operating systems are designed specifically to prevent apps from reading each other’s data. Overcoming the sandboxing on iOS (via Keychain Access Groups) and Android (via SharedPreferences and Keystore) is architecturally delicate. This requires deep native knowledge; when enterprise tech leaders hire mobile developers for secure cross-platform apps, they must ensure the team understands OS-level security boundaries rather than relying on high-level JavaScript wrappers.

    WHAT WENT WRONG

    Our initial approach was to find an existing cross-platform bridge. We evaluated several open-source libraries, but quickly uncovered severe security risks and architectural incompatibilities.

    First, we looked into libraries designed for shared group preferences. We discovered that many of these solutions relied on Android’s External Storage (publicly accessible folders) to pass data between apps. Storing authentication tokens in public directories is an unacceptable security risk for a financial application. Furthermore, this approach is entirely deprecated in modern Android environments (SDK 30+).

    Second, we faced severe encryption mismatches. To share data securely without public folders, the payload had to be encrypted. However, the legacy Cordova app and the modern React Native app utilized completely different default encryption transformations. When we attempted to pass an encrypted token, the decryption process failed.

    Third, the legacy Cordova architecture relied on KeyStore plugins that had not been maintained in over six years. The out-of-the-box Java code in these plugins did not support Android’s sharedUserId context, preventing inter-app data access.

    Ultimately, these constraints created a unidirectional data flow block. Initially, we could configure the Cordova application to read tokens written by React Native, but the React Native application could not reliably decrypt or read the tokens written by the legacy Cordova system.

    HOW WE APPROACHED THE SOLUTION

    We immediately discarded any solution involving external or public storage. The only secure path forward was to leverage official OS mechanisms for inter-app data sharing: App Groups and shared Keychain on iOS, and sharedUserId on Android.

    On iOS, the solution was relatively straightforward using App Groups and sharing the Keychain access group, allowing both apps to read the same securely encrypted keychain items.

    Android proved far more complex. We determined that the safest way to expose the Cordova SharedPreferences to the React Native app—without exposing the data to the rest of the operating system—was to execute a custom native context switch. We decided to build a custom Native Module in React Native to leverage the createPackageContext method.

    To solve the encryption mismatch, we had to dig into the native layer. We audited the legacy Cordova plugin’s source code and identified its encryption standard. We then manually overrode the React Native implementation to use the exact same algorithm: RSA/ECB/PKCS1Padding.

    Finally, to enable the legacy Cordova app to participate in this shared context, we had to manually patch the outdated Java code in the Cordova KeyStore plugin. We created an internally maintained fork of the plugin to support the shared user context during the migration phase.

    FINAL IMPLEMENTATION

    The final architecture successfully bridged the two ecosystems without compromising the mobile sandbox.

    For the Android implementation, we first ensured both applications shared the same signing certificate and defined the same android:sharedUserId in their respective manifests.

    Next, we implemented a custom Native Module in React Native to access the legacy app’s data securely. By utilizing createPackageContext, the modern app could request a context matching the legacy app’s package name, granting access to its isolated SharedPreferences.

    Here is a generic representation of the Java implementation used in our custom React Native module:

    ReactApplicationContext reactContext = getReactApplicationContext();
    try {
        Context legacyContext = reactContext.createPackageContext(
            "com.legacy.enterpriseapp",
            Context.CONTEXT_IGNORE_SECURITY
        );    
        SharedPreferences sharedPrefs = legacyContext.getSharedPreferences(
            "LegacySecurePrefs",
            Context.MODE_PRIVATE
        );    
        String encryptedToken = sharedPrefs.getString("auth_tokens", null);    
        if (encryptedToken != null) {
            // Proceed to decrypt using RSA/ECB/PKCS1Padding
            String decryptedToken = decryptToken(encryptedToken);
            // Pass token back to JS bridge
        }
    } catch (PackageManager.NameNotFoundException e) {
        // Handle scenario where legacy app is not installed
    }
    

    To ensure successful decryption, the decryptToken method was explicitly configured to use Cipher.getInstance(“RSA/ECB/PKCS1Padding”), matching the legacy environment precisely. This architectural alignment is crucial; when decision-makers hire react native developers for legacy modernization, having engineers who can confidently write and patch native Android and iOS code bridges the gap between old and new systems.

    LESSONS FOR ENGINEERING TEAMS

    • Avoid Public Storage for Secrets: Never use external or public device storage to share authentication tokens between applications, regardless of the libraries suggesting it. It is a severe security vulnerability.
    • Align Cryptographic Standards: Different frameworks have different default encryption algorithms and padding. Always explicitly define the cryptographic transformation (e.g., RSA/ECB/PKCS1Padding) when sharing encrypted data across different tech stacks.
    • Audit Legacy Dependencies: Legacy frameworks like Cordova often rely on abandoned plugins. Be prepared to fork, patch, and internally maintain these native modules to support modern security requirements.
    • Leverage Native Bridges Securely: Custom Native Modules are incredibly powerful. Utilizing mechanisms like createPackageContext with isolated SharedPreferences ensures data is shared only between applications signed by the same developer.
    • Plan for Seamless Decommissioning: Architect the shared context so that once the legacy application is fully retired, the modern application can seamlessly take over token lifecycle management without breaking active user sessions.

    WRAP UP

    Bridging a legacy Cordova application with a modern React Native app requires navigating complex native security sandboxes and cryptographic mismatches. By leveraging deep native OS features like custom package contexts, matching encryption algorithms, and securely patching legacy plugins, we delivered a seamless, frictionless authentication experience for the users. Building robust cross-platform security requires engineering maturity and a firm grasp of underlying mobile architectures. Whether you are modernizing legacy infrastructure or building a new product ecosystem, if you need to hire software developer resources with specialized native mobile expertise, contact us to discuss your project requirements.

    Social Hashtags

    #ReactNative #MobileSecurity #SSO #AppDevelopment #FintechEngineering #CrossPlatform #AndroidDev #iOSDev #SoftwareArchitecture #TechModernization #CyberSecurity #DevOps #EnterpriseApps #SecureCoding #TokenAuthentication

    Frequently Asked Questions

    Success Stories That Inspire

    See how our team takes complex business challenges and turns them into powerful, scalable digital solutions. From custom software and web applications to automation, integrations, and cloud-ready systems, each project reflects our commitment to innovation, performance, and long-term value.