react-native-wireguard-vpn-patched
v1.0.22-patch.2
Published
Production-ready React Native WireGuard VPN module with Android VPN permission handling and split tunneling support. Forked from react-native-wireguard-vpn v1.0.22.
Maintainers
Readme
react-native-wireguard-vpn-patched
A production-ready fork of react-native-wireguard-vpn (v1.0.22) with two critical Android fixes applied that are required for real-world VPN application deployment.
Author: Abdul Ahad — github.com/AbdulAHAD968
Base package: [email protected] by usama7365 (MIT)
Why This Fork Exists
The original react-native-wireguard-vpn package has two production-blocking issues on Android:
1. VPN Permission Not Requested
Android requires explicit user consent before any app can establish a VPN tunnel (VpnService.prepare()). The original connect() method skipped this check entirely, causing silent failures or crashes on devices where the permission had not been pre-granted. There was no mechanism for the app to know permission was needed, and no way to present the system dialog.
2. No Split Tunneling Support
The original module provided no way to exclude specific applications from the VPN tunnel. Android exposes VpnService.Builder.addDisallowedApplication() (API 21+) for this purpose, but it was not wired up in the native module. Any production VPN application is expected to offer this as a user-facing feature.
Changes in This Fork
Android — WireGuardVpnModule.kt
Fix 1: VPN Permission Request
Before the tunnel is brought up, VpnService.prepare() is now called. If the permission dialog needs to be shown, the native module:
- Launches the system intent via
startActivityForResult(intent, 1000) - Rejects the connect promise with error code
VPN_PERMISSION_REQUIRED - Returns immediately — the tunnel is not started
The caller handles VPN_PERMISSION_REQUIRED and re-calls connect() after the user grants permission (via AppState change detection or equivalent).
val intent = VpnService.prepare(reactApplicationContext)
if (intent != null) {
val activity = getCurrentActivity()
if (activity != null) {
activity.startActivityForResult(intent, 1000)
promise.reject("VPN_PERMISSION_REQUIRED", "Please accept the Android VPN permission dialog.")
return
}
}Fix 2: Split Tunneling via excludedApps
The connect() method now accepts an optional excludedApps string array. Each entry is an Android package name. The native module calls interfaceBuilder.excludeApplication(packageName) for each, routing those apps outside the VPN tunnel. Failures for individual packages are logged and non-fatal.
if (config.hasKey("excludedApps")) {
val excludedApps = config.getArray("excludedApps")?.toArrayList()
excludedApps?.forEach { app ->
(app as? String)?.let { packageName ->
try {
interfaceBuilder.excludeApplication(packageName)
} catch (e: Exception) {
println("Failed to exclude app $packageName: ${e.message}")
}
}
}
}Installation
npm install react-native-wireguard-vpn-patched
# or
yarn add react-native-wireguard-vpn-patchedIf you are migrating from react-native-wireguard-vpn, uninstall it first:
npm uninstall react-native-wireguard-vpnUsage
import WireGuard from 'react-native-wireguard-vpn-patched';
import { AppState } from 'react-native';
// Initialize once on app start
await WireGuard.initialize();
// Basic connection
await WireGuard.connect({
privateKey: '<client-private-key>',
publicKey: '<server-public-key>',
serverAddress: '203.0.113.1',
serverPort: 51820,
address: '10.64.0.2/32',
allowedIPs: ['0.0.0.0/0', '::/0'],
dns: ['1.1.1.1', '8.8.8.8'],
mtu: 1420,
});Handling VPN Permission (Android)
import { AppState } from 'react-native';
const connect = async () => {
try {
await WireGuard.connect(config);
// tunnel is up
} catch (err) {
if (err.code === 'VPN_PERMISSION_REQUIRED') {
// System dialog is now visible to the user.
// Listen for the app coming back to foreground, then retry.
const sub = AppState.addEventListener('change', async (state) => {
if (state === 'active') {
sub.remove();
await WireGuard.connect(config); // second call succeeds if user accepted
}
});
}
}
};Split Tunneling
Pass an excludedApps array of Android package names. Those apps will route through the device's regular internet connection, bypassing the VPN tunnel.
await WireGuard.connect({
// ...base config...
excludedApps: [
'com.google.android.apps.maps',
'com.example.bankingapp',
],
});API Reference
WireGuard.initialize(): Promise<void>
Initializes the WireGuard Go backend. Must be called once before connect().
WireGuard.connect(config: WireGuardConfig): Promise<void>
Establishes the VPN tunnel. Rejects with VPN_PERMISSION_REQUIRED on first run on Android if the user has not yet granted VPN permission.
WireGuard.disconnect(): Promise<void>
Tears down the active tunnel.
WireGuard.getStatus(): Promise<WireGuardStatus>
Returns the current tunnel state.
WireGuard.isSupported(): Promise<boolean>
Returns true if WireGuard is supported on the current device.
Types
interface WireGuardConfig {
privateKey: string;
publicKey: string;
serverAddress: string;
serverPort: number;
address?: string | string[]; // tunnel interface IP, e.g. "10.64.0.2/32"
allowedIPs: string[]; // routing rules, e.g. ["0.0.0.0/0", "::/0"]
dns?: string[];
mtu?: number;
presharedKey?: string;
excludedApps?: string[]; // Android only — package names to bypass VPN
}
interface WireGuardStatus {
isConnected: boolean;
tunnelState: 'ACTIVE' | 'INACTIVE' | 'CONNECTING' | 'DISCONNECTING' | 'ERROR' | 'UNKNOWN';
status: 'CONNECTED' | 'DISCONNECTED' | 'CONNECTING' | 'DISCONNECTING' | 'ERROR' | 'UNKNOWN';
error?: string;
}Requirements
| Requirement | Minimum Version | |------------------|-----------------| | React Native | 0.72.0 | | Android API | 21 (Android 5) | | compileSdkVersion| 34 | | Kotlin | 1.8.0 |
Platform Support
| Platform | Permission Fix | Split Tunneling | |----------|---------------|-----------------| | Android | Yes | Yes | | iOS | N/A (handled by OS) | Not yet |
iOS split tunneling is not implemented in this fork. The iOS implementation is unchanged from the upstream package.
Differences from Upstream
| Feature | react-native-wireguard-vpn | This fork |
|--------------------------------|------------------------------|-----------|
| Android VPN permission request | No | Yes |
| Split tunneling (excludedApps) | No | Yes |
| VPN_PERMISSION_REQUIRED error code | No | Yes |
| TypeScript: excludedApps field | No | Yes |
| Base version | 1.0.22 | 1.0.22-patch.2 |
License
MIT — same as the upstream package.
Original package copyright: usama7365
Fork modifications copyright: Abdul Ahad ([email protected])
