react-native-ssl-manager 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +319 -58
  2. package/android/build.gradle +23 -1
  3. package/android/src/main/java/com/usesslpinning/SslPinningFactory.kt +94 -0
  4. package/android/src/main/java/com/usesslpinning/UseSslPinningModuleImpl.kt +34 -0
  5. package/android/src/newarch/com/usesslpinning/UseSslPinningModule.kt +26 -0
  6. package/android/src/newarch/com/usesslpinning/UseSslPinningPackage.kt +36 -0
  7. package/android/src/oldarch/com/usesslpinning/UseSslPinningModule.kt +32 -0
  8. package/android/src/{main/java → oldarch}/com/usesslpinning/UseSslPinningPackage.kt +0 -1
  9. package/android/ssl-pinning-setup.gradle +148 -0
  10. package/app.plugin.js +293 -0
  11. package/expo-module.config.json +10 -0
  12. package/ios/SharedLogic.swift +247 -0
  13. package/ios/UseSslPinning.h +5 -0
  14. package/ios/{UseSslPinning.mm → UseSslPinningModule.mm} +9 -6
  15. package/ios/UseSslPinningModule.swift +65 -0
  16. package/lib/NativeUseSslPinning.d.ts +8 -0
  17. package/lib/NativeUseSslPinning.d.ts.map +1 -0
  18. package/lib/NativeUseSslPinning.js +4 -0
  19. package/lib/UseSslPinning.types.d.ts +17 -0
  20. package/lib/UseSslPinning.types.d.ts.map +1 -0
  21. package/lib/UseSslPinning.types.js +2 -0
  22. package/lib/index.d.ts +15 -0
  23. package/lib/index.d.ts.map +1 -0
  24. package/lib/index.js +58 -0
  25. package/package.json +83 -39
  26. package/react-native-ssl-manager.podspec +87 -38
  27. package/react-native.config.js +34 -0
  28. package/scripts/build.sh +52 -0
  29. package/src/NativeUseSslPinning.ts +9 -0
  30. package/src/UseSslPinning.types.ts +17 -0
  31. package/src/index.tsx +53 -33
  32. package/android/src/main/java/com/usesslpinning/UseSslPinningFactory.kt +0 -50
  33. package/android/src/main/java/com/usesslpinning/UseSslPinningModule.kt +0 -45
  34. package/ios/UseSslPinning-Bridging-Header.h +0 -2
  35. package/ios/UseSslPinning.swift +0 -169
@@ -0,0 +1,32 @@
1
+ package com.usesslpinning
2
+
3
+ import android.content.Context
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.facebook.react.bridge.ReactMethod
7
+ import com.facebook.react.bridge.Promise
8
+ import com.facebook.react.modules.network.OkHttpClientProvider
9
+ import com.facebook.react.module.annotations.ReactModule
10
+
11
+ @ReactModule(name = UseSslPinningModuleImpl.NAME)
12
+ class UseSslPinningModule(reactContext: ReactApplicationContext) :
13
+ ReactContextBaseJavaModule(reactContext) {
14
+
15
+ init {
16
+ UseSslPinningModuleImpl.initialize(reactContext)
17
+ }
18
+
19
+ override fun getName(): String {
20
+ return UseSslPinningModuleImpl.NAME
21
+ }
22
+
23
+ @ReactMethod
24
+ fun setUseSSLPinning(usePinning: Boolean, promise: Promise) {
25
+ UseSslPinningModuleImpl.setUseSSLPinning(reactApplicationContext, usePinning, promise)
26
+ }
27
+
28
+ @ReactMethod
29
+ fun getUseSSLPinning(promise: Promise) {
30
+ UseSslPinningModuleImpl.getUseSSLPinning(reactApplicationContext, promise)
31
+ }
32
+ }
@@ -5,7 +5,6 @@ import com.facebook.react.bridge.NativeModule
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
6
  import com.facebook.react.uimanager.ViewManager
7
7
 
8
-
9
8
  class UseSslPinningPackage : ReactPackage {
10
9
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
10
  return listOf(UseSslPinningModule(reactContext))
@@ -0,0 +1,148 @@
1
+ def findSslConfigFile() {
2
+ def candidates = [
3
+ file("${project.rootDir}/../ssl_config.json"), // repo root (android/ là thư mục con)
4
+ file("${project.rootDir}/ssl_config.json"), // android root
5
+ file("${projectDir}/../../ssl_config.json"), // monorepo fallback
6
+ file("${projectDir}/../ssl_config.json")
7
+ ]
8
+ for (f in candidates) {
9
+ if (f.exists()) return f
10
+ }
11
+ return null
12
+ }
13
+
14
+ def ensureAssetsDir() {
15
+ def d = file("${projectDir}/src/main/assets")
16
+ if (!d.exists()) d.mkdirs()
17
+ return d
18
+ }
19
+
20
+ def assetsDir = file("${projectDir}/src/main/assets")
21
+ def destFile = file("${projectDir}/src/main/assets/ssl_config.json")
22
+
23
+ plugins.withId('com.android.application') {
24
+
25
+ // ===== AGP 7/8: androidComponents DSL =====
26
+ if (project.extensions.findByName("androidComponents") != null) {
27
+ androidComponents {
28
+ onVariants(selector().all()) { variant -> // <-- Sửa chỗ này
29
+ def vName = variant.name
30
+ def vNameCap = vName.substring(0,1).toUpperCase() + vName.substring(1)
31
+ def taskName = "copySslConfig${vNameCap}"
32
+
33
+ tasks.register(taskName, Copy) {
34
+ group = "SSL Pinning"
35
+ description = "Copy ssl_config.json to assets for ${vName}"
36
+
37
+ def sourceFile = findSslConfigFile()
38
+
39
+ onlyIf {
40
+ if (sourceFile) {
41
+ println "📋 SSL Config found: ${sourceFile.absolutePath}"
42
+ true
43
+ } else {
44
+ println "⚠️ SSL Config not found in expected locations"
45
+ false
46
+ }
47
+ }
48
+
49
+ doFirst {
50
+ ensureAssetsDir()
51
+ println "🔄 Copying SSL config for variant: ${vName}"
52
+ println "📂 To: ${assetsDir}"
53
+ }
54
+
55
+ from { sourceFile }
56
+ into assetsDir
57
+
58
+ doLast {
59
+ if (destFile.exists()) {
60
+ println "✅ SSL config copied: ${destFile} (${destFile.length()} bytes)"
61
+ } else {
62
+ println "❌ SSL config copy failed"
63
+ }
64
+ }
65
+ }
66
+
67
+ // Hook chắc chắn cho assemble/install/mergeAssets
68
+ tasks.matching { it.name == "merge${vNameCap}Assets" }.configureEach { dependsOn taskName }
69
+ tasks.matching { it.name == "install${vNameCap}" }.configureEach { dependsOn taskName }
70
+ tasks.matching { it.name == "assemble${vNameCap}" }.configureEach { dependsOn taskName }
71
+ }
72
+ }
73
+
74
+ } else {
75
+ // ===== Fallback: API cũ applicationVariants =====
76
+ afterEvaluate {
77
+ android.applicationVariants.all { variant ->
78
+ def bt = variant.buildType.name
79
+ def fl = variant.flavorName ?: ""
80
+ def vNameCap = (fl ? "${fl.capitalize()}${bt.capitalize()}" : bt.capitalize())
81
+ def taskName = "copySslConfig${vNameCap}"
82
+
83
+ def copyTask = tasks.create(taskName, Copy) {
84
+ group = "SSL Pinning"
85
+ description = "Copy ssl_config.json to assets for ${vNameCap}"
86
+
87
+ def sourceFile = findSslConfigFile()
88
+
89
+ onlyIf {
90
+ if (sourceFile) {
91
+ println "📋 SSL Config found: ${sourceFile.absolutePath}"
92
+ true
93
+ } else {
94
+ println "⚠️ SSL Config not found in expected locations"
95
+ false
96
+ }
97
+ }
98
+
99
+ doFirst {
100
+ ensureAssetsDir()
101
+ println "🔄 Copying SSL config for variant: ${vNameCap}"
102
+ println "📂 To: ${assetsDir}"
103
+ }
104
+
105
+ from { sourceFile }
106
+ into assetsDir
107
+
108
+ doLast {
109
+ if (destFile.exists()) {
110
+ println "✅ SSL config copied: ${destFile} (${destFile.length()} bytes)"
111
+ } else {
112
+ println "❌ SSL config copy failed"
113
+ }
114
+ }
115
+ }
116
+
117
+ try {
118
+ if (variant.hasProperty("mergeAssetsProvider")) {
119
+ variant.mergeAssetsProvider.configure { dependsOn copyTask }
120
+ } else {
121
+ tasks.named("merge${vNameCap}Assets").configure { dependsOn copyTask }
122
+ }
123
+ } catch (ignored) { }
124
+ try { tasks.named("install${vNameCap}").configure { dependsOn copyTask } } catch (ignored) { }
125
+ try { tasks.named("assemble${vNameCap}").configure { dependsOn copyTask } } catch (ignored) { }
126
+ }
127
+ }
128
+ }
129
+
130
+ // ===== Utility =====
131
+ tasks.register("checkSslConfig") {
132
+ group = "SSL Pinning"
133
+ description = "Check SSL config file status"
134
+ doLast {
135
+ def source = findSslConfigFile()
136
+ println "\n📋 SSL Pinning Configuration Status:"
137
+ println " Source config: " + (source ? "✅ Found at ${source.absolutePath}" : "❌ Not found")
138
+ println " Assets config: " + (destFile.exists() ? "✅ Found at ${destFile.absolutePath}" : "❌ Not found")
139
+ if (!source) {
140
+ println "💡 Create ssl_config.json tại repo root (ngang 'android/') hoặc trong 'android/'."
141
+ } else if (!destFile.exists()) {
142
+ println "💡 Run: ./gradlew copySslConfigDebug (hoặc biến thể tương ứng)"
143
+ } else {
144
+ println "✅ SSL config is properly setup"
145
+ }
146
+ }
147
+ }
148
+ }
package/app.plugin.js ADDED
@@ -0,0 +1,293 @@
1
+ const {
2
+ withInfoPlist,
3
+ withDangerousMod,
4
+ withXcodeProject,
5
+ withAndroidManifest,
6
+ withMainApplication,
7
+ } = require('@expo/config-plugins');
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ /**
12
+ * Expo Config Plugin for react-native-ssl-manager
13
+ * Automatically configures SSL pinning for Expo projects
14
+ */
15
+ function withSslManager(config, options = {}) {
16
+ const {
17
+ enableAndroid = true,
18
+ enableIOS = true,
19
+ sslConfigPath = 'ssl_config.json',
20
+ } = options;
21
+
22
+ // Add Android configuration
23
+ if (enableAndroid) {
24
+ config = withAndroidSslPinning(config);
25
+ config = withAndroidMainApplication(config);
26
+ config = withAndroidAssets(config, { sslConfigPath });
27
+ }
28
+
29
+ // Add iOS configuration
30
+ if (enableIOS) {
31
+ config = withIOSSslPinning(config);
32
+ config = withIosAssets(config, { sslConfigPath });
33
+ }
34
+
35
+ return config;
36
+ }
37
+
38
+ /**
39
+ * Configure Android SSL pinning
40
+ */
41
+ function withAndroidSslPinning(config) {
42
+ return withAndroidManifest(config, (config) => {
43
+ const manifest = config.modResults;
44
+
45
+ // Add required permissions
46
+ if (!manifest.manifest['uses-permission']) {
47
+ manifest.manifest['uses-permission'] = [];
48
+ }
49
+
50
+ const permissions = Array.isArray(manifest.manifest['uses-permission'])
51
+ ? manifest.manifest['uses-permission']
52
+ : [manifest.manifest['uses-permission']];
53
+
54
+ // Add INTERNET permission if not exists
55
+ if (
56
+ !permissions.find(
57
+ (p) => p.$['android:name'] === 'android.permission.INTERNET'
58
+ )
59
+ ) {
60
+ permissions.push({
61
+ $: {
62
+ 'android:name': 'android.permission.INTERNET',
63
+ },
64
+ });
65
+ }
66
+
67
+ manifest.manifest['uses-permission'] = permissions;
68
+ return config;
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Configure Android MainApplication - No longer needed as autolinking handles package registration
74
+ * Keeping function for backward compatibility but removing auto-registration to avoid duplicates
75
+ */
76
+ function withAndroidMainApplication(config) {
77
+ // No longer auto-register package - let autolinking handle it
78
+ // This prevents duplicate module registration errors
79
+ return config;
80
+ }
81
+
82
+ /**
83
+ * Auto-copy SSL config to Android assets
84
+ */
85
+ function withAndroidAssets(config, options) {
86
+ return withDangerousMod(config, [
87
+ 'android',
88
+ async (config) => {
89
+ const { sslConfigPath = 'ssl_config.json' } = options;
90
+
91
+ try {
92
+ const projectRoot = config.modRequest.projectRoot;
93
+ const sourceConfigPath = path.resolve(projectRoot, sslConfigPath);
94
+
95
+ if (fs.existsSync(sourceConfigPath)) {
96
+ const assetsDir = path.join(
97
+ config.modRequest.platformProjectRoot,
98
+ 'app/src/main/assets'
99
+ );
100
+
101
+ // Create assets directory if it doesn't exist
102
+ if (!fs.existsSync(assetsDir)) {
103
+ fs.mkdirSync(assetsDir, { recursive: true });
104
+ }
105
+
106
+ // Copy SSL config to assets
107
+ const targetPath = path.join(assetsDir, 'ssl_config.json');
108
+ fs.copyFileSync(sourceConfigPath, targetPath);
109
+ } else {
110
+ console.warn(`⚠️ SSL config file not found at: ${sourceConfigPath}`);
111
+ console.warn(
112
+ '💡 Place ssl_config.json in your project root for auto-setup'
113
+ );
114
+ }
115
+ } catch (error) {
116
+ console.warn('⚠️ Failed to auto-copy SSL config to assets:', error);
117
+ }
118
+
119
+ return config;
120
+ },
121
+ ]);
122
+ }
123
+
124
+ /**
125
+ * Configure iOS SSL pinning - No Info.plist modification needed
126
+ * SSL pinning is handled by SharedLogic.swift at runtime
127
+ */
128
+ function withIOSSslPinning(config) {
129
+ // No Info.plist modifications needed
130
+ // SSL pinning is initialized at runtime by SharedLogic.swift
131
+
132
+ return config;
133
+ }
134
+
135
+ /**
136
+ * Auto-copy SSL config to iOS bundle resources and add to Xcode project
137
+ */
138
+ function withIosAssets(config, options) {
139
+ // First copy the file
140
+ config = withDangerousMod(config, [
141
+ 'ios',
142
+ async (config) => {
143
+ const { sslConfigPath = 'ssl_config.json' } = options;
144
+
145
+ try {
146
+ const projectRoot = config.modRequest.projectRoot;
147
+ const sourceConfigPath = path.resolve(projectRoot, sslConfigPath);
148
+
149
+ if (fs.existsSync(sourceConfigPath)) {
150
+ // Create ios directory if it doesn't exist
151
+ const iosDir = path.join(projectRoot, 'ios');
152
+ if (!fs.existsSync(iosDir)) {
153
+ fs.mkdirSync(iosDir, { recursive: true });
154
+ }
155
+
156
+ // Copy ssl_config.json to ios directory
157
+ const targetConfigPath = path.join(iosDir, 'ssl_config.json');
158
+ fs.copyFileSync(sourceConfigPath, targetConfigPath);
159
+
160
+ // Also copy to app bundle directory for Xcode project
161
+ const appBundleDir = path.join(iosDir, config.modRequest.projectName);
162
+ const appBundleConfigPath = path.join(
163
+ appBundleDir,
164
+ 'ssl_config.json'
165
+ );
166
+ if (fs.existsSync(appBundleDir)) {
167
+ fs.copyFileSync(sourceConfigPath, appBundleConfigPath);
168
+ }
169
+ } else {
170
+ console.warn(`⚠️ SSL config file not found at: ${sourceConfigPath}`);
171
+ console.warn(
172
+ '💡 Place ssl_config.json in your project root for auto-setup'
173
+ );
174
+ }
175
+ } catch (error) {
176
+ console.warn('⚠️ Failed to copy SSL config:', error);
177
+ }
178
+
179
+ return config;
180
+ },
181
+ ]);
182
+
183
+ // Add to Xcode project programmatically - run in same withDangerousMod as file copy
184
+ config = withDangerousMod(config, [
185
+ 'ios',
186
+ async (config) => {
187
+ // First ensure file is copied to app bundle directory
188
+ const projectName = config.modRequest.projectName || 'exampleexpo';
189
+ const projectRoot = config.modRequest.projectRoot;
190
+ const sourceConfigPath = path.resolve(projectRoot, 'ssl_config.json');
191
+ const appBundleDir = path.join(
192
+ config.modRequest.platformProjectRoot,
193
+ projectName
194
+ );
195
+ const appBundleConfigPath = path.join(appBundleDir, 'ssl_config.json');
196
+
197
+ // Ensure SSL config is copied to app bundle directory
198
+ if (fs.existsSync(sourceConfigPath) && fs.existsSync(appBundleDir)) {
199
+ fs.copyFileSync(sourceConfigPath, appBundleConfigPath);
200
+ }
201
+
202
+ try {
203
+ const projectPath = path.join(
204
+ config.modRequest.platformProjectRoot,
205
+ `${projectName}.xcodeproj/project.pbxproj`
206
+ );
207
+ const sslConfigPath = appBundleConfigPath;
208
+
209
+ if (!fs.existsSync(projectPath) || !fs.existsSync(sslConfigPath)) {
210
+ console.warn(
211
+ '⚠️ Xcode project or SSL config not found, skipping automatic addition'
212
+ );
213
+ return config;
214
+ }
215
+
216
+ let projectContent = fs.readFileSync(projectPath, 'utf8');
217
+
218
+ // Check if already added
219
+ if (projectContent.includes('ssl_config.json')) {
220
+ return config;
221
+ }
222
+
223
+ // Generate unique IDs for the file
224
+ const fileRefId =
225
+ 'SSL' + Math.random().toString(36).substr(2, 24).toUpperCase();
226
+ const buildFileId =
227
+ 'SSL' + Math.random().toString(36).substr(2, 24).toUpperCase();
228
+
229
+ // Add file reference
230
+ const fileRefEntry = `\t\t${fileRefId} /* ssl_config.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = ssl_config.json; sourceTree = "<group>"; };`;
231
+ projectContent = projectContent.replace(
232
+ '/* End PBXFileReference section */',
233
+ fileRefEntry + '\n\t\t/* End PBXFileReference section */'
234
+ );
235
+
236
+ // Add build file
237
+ const buildFileEntry = `\t\t${buildFileId} /* ssl_config.json in Resources */ = {isa = PBXBuildFile; fileRef = ${fileRefId} /* ssl_config.json */; };`;
238
+ projectContent = projectContent.replace(
239
+ '/* End PBXBuildFile section */',
240
+ buildFileEntry + '\n\t\t/* End PBXBuildFile section */'
241
+ );
242
+
243
+ // Add to resources build phase
244
+ const resourcesPhaseMatch = projectContent.match(
245
+ /(\w+) \/\* Resources \*\/ = \{[^}]*files = \(([^)]*)\)/
246
+ );
247
+ if (resourcesPhaseMatch) {
248
+ const filesSection = resourcesPhaseMatch[2];
249
+ const newFilesSection =
250
+ filesSection +
251
+ `\t\t\t\t${buildFileId} /* ssl_config.json in Resources */,\n`;
252
+ projectContent = projectContent.replace(
253
+ `files = (${filesSection})`,
254
+ `files = (\n${newFilesSection}\t\t\t)`
255
+ );
256
+ }
257
+
258
+ // Add to main group
259
+ const mainGroupMatch = projectContent.match(
260
+ new RegExp(
261
+ `(\\w+) /\\* ${projectName} \\*/ = \\{[^}]*children = \\(([^)]*)\\)`
262
+ )
263
+ );
264
+ if (mainGroupMatch) {
265
+ const childrenSection = mainGroupMatch[2];
266
+ const newChildrenSection =
267
+ childrenSection + `\t\t\t\t${fileRefId} /* ssl_config.json */,\n`;
268
+ projectContent = projectContent.replace(
269
+ `children = (${childrenSection})`,
270
+ `children = (\n${newChildrenSection}\t\t\t)`
271
+ );
272
+ }
273
+
274
+ // Write back to file
275
+ fs.writeFileSync(projectPath, projectContent);
276
+ } catch (error) {
277
+ console.warn(
278
+ '⚠️ Failed to add SSL config to Xcode project:',
279
+ error.message
280
+ );
281
+ console.warn(
282
+ '💡 File copied to ios/ directory, manual Xcode setup may be needed'
283
+ );
284
+ }
285
+
286
+ return config;
287
+ },
288
+ ]);
289
+
290
+ return config;
291
+ }
292
+
293
+ module.exports = withSslManager;
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "react-native-ssl-manager",
3
+ "platforms": ["apple"],
4
+ "android": {
5
+ "modules": []
6
+ },
7
+ "apple": {
8
+ "modules": ["UseSslPinningExpoModule"]
9
+ }
10
+ }