Files
01c352636c feat(mobile): upgrade Expo SDK to v55 (#7433)
* feat(mobile): upgrade Expo SDK to v55

Upgrade Expo SDK from v54 to v55, along with React Native 0.83.4,
React 19.2, and all related dependencies.

Key changes:
- Refactored @safe-global/theme to use sub-path exports to fix RN
  import resolution issues
- Added react-native patch for Appearance.setColorScheme bug
  (facebook/react-native#08d176453) not yet in 0.83.4 release
- Replaced Tamagui Skeleton with custom SafeSkeleton component
- Updated expo config plugins for SDK 55 compatibility

* fix: downgrade eslint-config-next to v15 to match next.js version

eslint-config-next v16 requires next v16 but the project uses next
v15.5.8. The v16 package tries to import next/dist/compiled/babel/
eslint-parser which doesn't exist in next v15.

* fix: migrate packages away from eslint-config-next

eslint-config-next requires next at runtime for its babel parser,
but next is only installed in apps/web. After the Expo SDK v55
upgrade changed dependency hoisting, the parser became unreachable.

- Create shared config/eslint/base.mjs using direct plugin imports
- Migrate store, theme, utils, and tx-builder to the shared config
- Add packageExtensions in .yarnrc.yml so eslint-config-next in
  apps/web can still resolve next as a peer dependency

* chore: align react 19.2 across workspaces and fix MUI peer dep

- Bump react/react-dom to 19.2.0 in web and tx-builder to match
  mobile
- Add @mui/material as optional peer dependency in theme package to
  avoid duplicate installations causing type conflicts

* chore(mobile): update storybook on mobile to v10

* fix(web): resolve @emotion/react hoisting issue after Expo SDK 55 upgrade

Yarn 4's node-modules hoisting shifted after dependency graph changes,
leaving @emotion/react only in apps/web/node_modules while @mui/styled-engine
remained at root. Add webpack resolve aliases to ensure Next.js finds emotion
packages from the web workspace.

* fix(web): resolve @emotion hoisting issue in Jest config

Same hoisting fix as next.config.mjs but for Jest's module resolution.
Add moduleNameMapper entries for @emotion/react and @emotion/styled.

* fix(tx-builder): prettier

* fix: resolve type-check errors after Expo SDK 55 upgrade

- Fix BlurView tint prop: map ColorSchemeName to valid BlurTint values
- Replace @storybook/test imports with storybook/test (merged in v10)
- Align @types/react (~19.2.10) and @types/react-dom (~19.2.0) across
  all workspaces to prevent duplicate @mui/material type mismatches
- Spread visuallyHidden into sx prop for @types/react 19.2.x compat
- Add @types/react, @types/react-dom, storybook to yarn version checker

* chore: align more dependencies

* chore(web): update storybook snapshot

* fix(mobile): normalize color scheme and deduplicate MUI/emotion packages

- Normalize useTheme colorScheme to 'light' | 'dark', never 'unspecified'
  so TamaguiProvider always receives a valid theme name
- Update ColorScheme type to exclude 'unspecified'
- Remove redundant ?? 'light' fallbacks in SafeThemeProvider
- Align @emotion/* and @mui/icons-material versions across workspaces
  to prevent Yarn from creating duplicate lockfile resolutions
- Remove emotion webpack/Jest aliases (no longer needed after dedup)
- Add @emotion/react, @emotion/styled, @mui/material, @mui/icons-material
  to yarn version consistency checker

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-03-18 09:29:16 +01:00
..
2025-07-29 13:10:51 +02:00

SSL Pinning Expo Config Plugin

This plugin implements SSL certificate pinning for React Native apps using Expo, supporting both iOS and Android platforms.

Features

  • iOS SSL Pinning: Uses TrustKit library for secure certificate validation
  • Android SSL Pinning: Uses OkHttp's built-in certificate pinner
  • Automatic native code modification: Handles all necessary iOS and Android code changes

How SSL Pinning Works

SSL pinning enhances security by embedding specific certificate hashes directly into your app code. This prevents man-in-the-middle attacks even if an attacker manages to install malicious certificates on the device or compromises a certificate authority.

Getting Certificate Hashes

Before configuring the plugin, you need to obtain the public key hashes for your domains:

# This script automatically extracts certificates and provides ready-to-use SSL pinning configuration
cd apps/mobile
node scripts/getCertificates.js safe-client.safe.global

# Multiple domains at once
node scripts/getCertificates.js safe-client.safe.global safe-client.staging.5afe.dev

Method 2: Manual OpenSSL Commands

Step 1: Get Full Certificate Chain

# Get the complete certificate chain
openssl s_client -servername example.com -connect example.com:443 -showcerts > fullchain.pem

Step 2: Extract Specific Certificates

# Extract leaf certificate (first certificate)
awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' fullchain.pem | sed -n '1,/END CERTIFICATE/p' > leaf.pem

# Extract intermediate certificate (second certificate)
awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' fullchain.pem | sed -n '2,/END CERTIFICATE/p' > intermediate.pem

# Extract root certificate (last certificate)
awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' fullchain.pem | tail -n +2 > root.pem

Step 3: Generate Certificate Hashes

# Leaf certificate hash
openssl x509 -in leaf.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

# Intermediate certificate hash (recommended for backup)
openssl x509 -in intermediate.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

# Root certificate hash
openssl x509 -in root.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Configuration

1. Configure SSL Pinning Domains

Update apps/mobile/app.config.js to include your domains and certificate hashes in the SSL pinning plugin configuration:

[
  './expo-plugins/ssl-pinning/withSSLPinning.js',
  {
    domains: {
      'api.safeglobal.io': [
        'PRIMARY_CERT_HASH_BASE64'
      ],
      'dev-api.safeglobal.io': [
        'DEV_PRIMARY_CERT_HASH_BASE64'
      ],
    },
  },
],

2. Plugin Configuration

The plugin is already integrated into the app configuration. Simply update the domains object with your certificate hashes.

Environment Configuration

The plugin uses the same domain configuration for all builds. You can include both production and development/staging domains in the same configuration:

  • All configured domains will be pinned across all build variants
  • Include both production and staging/development domains as needed
  • Each domain can have multiple certificate hashes for backup

Testing SSL Pinning

Testing Valid Certificates

  1. Build and run your app
  2. Make network requests to pinned domains
  3. Requests should work normally

Testing Invalid Certificates

  1. Change one of the certificate hashes to an invalid value (e.g., all A's)
  2. Rebuild the app completely
  3. Clear app cache/data or reinstall
  4. Network requests to pinned domains should fail

Important: iOS maintains a TLS session cache, so you may need to:

  • Delete and reinstall the app
  • Reset the iOS Simulator
  • Wait for the cache to expire

Using Proxy Tools

You can test SSL pinning using tools like:

When SSL pinning is working correctly, requests through these proxies should fail.

References