@hituchhimpa/react-native-auth-vault 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hituchhimpa7
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # ๐Ÿ›ก๏ธ React Native Auth Vault
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@hituchhimpa/react-native-auth-vault.svg?style=flat-square)](https://www.npmjs.com/package/@hituchhimpa/react-native-auth-vault)
4
+ [![license](https://img.shields.io/npm/l/@hituchhimpa/react-native-auth-vault.svg?style=flat-square)](LICENSE)
5
+ [![platform](https://img.shields.io/badge/platform-ios%20%7C%20android-blue.svg?style=flat-square)](#)
6
+
7
+ A premium, native-first security and authentication library for React Native. Provides secure, hardware-backed cryptographic storage and runtime security auditing for enterprise-grade mobile applications.
8
+
9
+ ---
10
+
11
+ ## โœจ Features
12
+
13
+ * ๐Ÿ”‘ **Hardware-Backed Encryption**: Private keys are generated and stored securely inside the **Android Keystore (TEE/StrongBox)** and **iOS Secure Enclave**.
14
+ * ๐Ÿ‘ค **Biometric Protection**: FaceID, TouchID, and Android BiometricPrompt integration with customizable user prompts.
15
+ * โšก **Optional Biometrics**: Supports both biometric-authenticated operations and high-performance silent hardware encryption (no prompts).
16
+ * ๐Ÿ›ก๏ธ **Security Auditing**: Run real-time checks to inspect device integrity (e.g. root/jailbreak detection, device passcode setup, biometrics status).
17
+ * ๐Ÿงต **Thread Safe & Native**: Constructed using React Native's modern architecture, executing complex cryptographic tasks on native threads.
18
+
19
+ ---
20
+
21
+ ## ๐Ÿ“ฆ Installation
22
+
23
+ ```sh
24
+ npm install @hituchhimpa/react-native-auth-vault
25
+ # or
26
+ yarn add @hituchhimpa/react-native-auth-vault
27
+ ```
28
+
29
+ For iOS, install the pods:
30
+ ```sh
31
+ cd ios && pod install
32
+ ```
33
+
34
+ ---
35
+
36
+ ## ๐Ÿš€ Usage
37
+
38
+ ### 1. Store & Retrieve with Biometric Protection
39
+ Prompt the user for biometrics (Face ID/Touch ID/Fingerprint/Passcode) to unlock access to the stored key:
40
+
41
+ ```typescript
42
+ import { AuthVault } from '@hituchhimpa/react-native-auth-vault';
43
+
44
+ // Store a value securely (triggers biometric prompt)
45
+ const saveSecureToken = async (token: string) => {
46
+ try {
47
+ const success = await AuthVault.setItem(
48
+ 'user_token',
49
+ token,
50
+ 'Scan fingerprint to secure your credentials'
51
+ );
52
+ if (success) console.log('Stored securely!');
53
+ } catch (error) {
54
+ console.error('Storage failed:', error);
55
+ }
56
+ };
57
+
58
+ // Retrieve a value (triggers biometric prompt)
59
+ const getSecureToken = async () => {
60
+ try {
61
+ const token = await AuthVault.getItem(
62
+ 'user_token',
63
+ 'Scan fingerprint to access your account'
64
+ );
65
+ console.log('Retrieved Token:', token);
66
+ } catch (error) {
67
+ console.error('Failed to unlock token:', error);
68
+ }
69
+ };
70
+ ```
71
+
72
+ ### 2. Silent Hardware-Backed Storage (Optional Biometrics)
73
+ Encrypt and store keys using hardware cryptoprocessors (Secure Enclave / TEE) **silently** without prompting the user. Perfect for API request signing, background session tokens, or caching:
74
+
75
+ ```typescript
76
+ // Pass an empty string `""` as the prompt to bypass the biometric UI
77
+ const saveSilentToken = async (token: string) => {
78
+ await AuthVault.setItem('api_key', token, '');
79
+ };
80
+
81
+ const getSilentToken = async () => {
82
+ const token = await AuthVault.getItem('api_key', '');
83
+ return token; // Returns the token silently
84
+ };
85
+ ```
86
+
87
+ ### 3. Device Security Auditing
88
+ Get security metrics to decide whether your app should run or restrict sensitive actions:
89
+
90
+ ```typescript
91
+ const checkDeviceSecurity = () => {
92
+ const audit = AuthVault.audit();
93
+ console.log(audit);
94
+ /*
95
+ Output:
96
+ {
97
+ isRooted: false, // Root/Jailbreak status
98
+ hasPin: true, // Device lock (PIN/Password) setup
99
+ biometricsEnabled: true, // Biometrics enrollment status
100
+ hardwareBacked: true // Key storage hardware backing status
101
+ }
102
+ */
103
+ };
104
+ ```
105
+
106
+ ---
107
+
108
+ ## ๐Ÿ› ๏ธ API Reference
109
+
110
+ | Method | Type | Description |
111
+ | :--- | :--- | :--- |
112
+ | `setItem(key, value, prompt)` | `Promise<boolean>` | Encrypts and saves `value` locally. Pass non-empty `prompt` for biometrics, or `""` for silent encryption. |
113
+ | `getItem(key, prompt)` | `Promise<string \| null>` | Decrypts and retrieves `value`. Pass non-empty `prompt` for biometrics, or `""` for silent decryption. |
114
+ | `removeItem(key)` | `Promise<boolean>` | Deletes the stored key and encrypted value from device. |
115
+ | `encrypt(plainText, prompt)` | `Promise<string>` | Encrypts raw text and returns a base64 cipher payload. |
116
+ | `decrypt(base64Text, prompt)` | `Promise<string>` | Decrypts a base64 cipher payload back to plain text. |
117
+ | `audit()` | `Object` | Runs security checks on the device hardware and environment. |
118
+
119
+ ---
120
+
121
+ ## ๐Ÿ‘ค Author
122
+
123
+ Developed and maintained by [Hitesh Chhimpa](https://github.com/hituchhimpa7).
124
+
125
+ ---
126
+
127
+ ## ๐Ÿ“„ License
128
+
129
+ MIT
@@ -0,0 +1,20 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "ReactNativeAuthVault"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/hituchhimpa7/react-native-auth-vault.git.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+ install_modules_dependencies(s)
20
+ end
@@ -0,0 +1,68 @@
1
+ buildscript {
2
+ ext.ReactNativeAuthVault = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36,
6
+ targetSdkVersion: 36
7
+ ]
8
+
9
+ ext.getExtOrDefault = { prop ->
10
+ if (rootProject.ext.has(prop)) {
11
+ return rootProject.ext.get(prop)
12
+ }
13
+
14
+ return ReactNativeAuthVault[prop]
15
+ }
16
+
17
+ repositories {
18
+ google()
19
+ mavenCentral()
20
+ }
21
+
22
+ dependencies {
23
+ classpath "com.android.tools.build:gradle:8.7.2"
24
+ // noinspection DifferentKotlinGradleVersion
25
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
26
+ }
27
+ }
28
+
29
+
30
+ apply plugin: "com.android.library"
31
+ apply plugin: "kotlin-android"
32
+
33
+ apply plugin: "com.facebook.react"
34
+
35
+ android {
36
+ namespace "com.hituchhimpa.reactnativeauthvault"
37
+
38
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
39
+
40
+ defaultConfig {
41
+ minSdkVersion getExtOrDefault("minSdkVersion")
42
+ targetSdkVersion getExtOrDefault("targetSdkVersion")
43
+ }
44
+
45
+ buildFeatures {
46
+ buildConfig true
47
+ }
48
+
49
+ buildTypes {
50
+ release {
51
+ minifyEnabled false
52
+ }
53
+ }
54
+
55
+ lint {
56
+ disable "GradleCompatible"
57
+ }
58
+
59
+ compileOptions {
60
+ sourceCompatibility JavaVersion.VERSION_1_8
61
+ targetCompatibility JavaVersion.VERSION_1_8
62
+ }
63
+ }
64
+
65
+ dependencies {
66
+ implementation "com.facebook.react:react-android"
67
+ implementation "androidx.biometric:biometric:1.2.0-alpha05"
68
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,190 @@
1
+ package com.hituchhimpa.reactnativeauthvault
2
+
3
+ import android.content.Context
4
+ import android.os.Build
5
+ import android.security.keystore.KeyGenParameterSpec
6
+ import android.security.keystore.KeyProperties
7
+ import android.util.Base64
8
+ import androidx.biometric.BiometricManager
9
+ import androidx.biometric.BiometricPrompt
10
+ import androidx.core.content.ContextCompat
11
+ import androidx.fragment.app.FragmentActivity
12
+ import java.security.KeyStore
13
+ import javax.crypto.Cipher
14
+ import javax.crypto.KeyGenerator
15
+ import javax.crypto.SecretKey
16
+ import javax.crypto.spec.GCMParameterSpec
17
+
18
+ class CryptoEngine(private val context: Context) {
19
+ private val keyAliasBiometric = "AuthVaultKey_Biometric"
20
+ private val keyAliasNonBiometric = "AuthVaultKey_NonBiometric"
21
+ private val androidKeyStore = "AndroidKeyStore"
22
+
23
+ init {
24
+ generateKey(keyAliasBiometric, true)
25
+ generateKey(keyAliasNonBiometric, false)
26
+ }
27
+
28
+ private fun generateKey(alias: String, requireAuth: Boolean) {
29
+ val keyStore = KeyStore.getInstance(androidKeyStore)
30
+ keyStore.load(null)
31
+
32
+ if (!keyStore.containsAlias(alias)) {
33
+ val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, androidKeyStore)
34
+
35
+ val builder = KeyGenParameterSpec.Builder(
36
+ alias,
37
+ KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
38
+ )
39
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
40
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
41
+ .setUserAuthenticationRequired(requireAuth)
42
+
43
+ if (requireAuth) {
44
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
45
+ builder.setUserAuthenticationParameters(
46
+ 0,
47
+ KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL
48
+ )
49
+ } else {
50
+ @Suppress("DEPRECATION")
51
+ builder.setUserAuthenticationValidityDurationSeconds(-1)
52
+ }
53
+ }
54
+
55
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
56
+ if (context.packageManager.hasSystemFeature("android.hardware.strongbox_keystore")) {
57
+ builder.setIsStrongBoxBacked(true)
58
+ }
59
+ }
60
+
61
+ keyGenerator.init(builder.build())
62
+ keyGenerator.generateKey()
63
+ }
64
+ }
65
+
66
+ private fun getCipher(): Cipher {
67
+ return Cipher.getInstance("${KeyProperties.KEY_ALGORITHM_AES}/${KeyProperties.BLOCK_MODE_GCM}/${KeyProperties.ENCRYPTION_PADDING_NONE}")
68
+ }
69
+
70
+ private fun getSecretKey(alias: String): SecretKey {
71
+ val keyStore = KeyStore.getInstance(androidKeyStore)
72
+ keyStore.load(null)
73
+ return keyStore.getKey(alias, null) as SecretKey
74
+ }
75
+
76
+ fun encrypt(activity: FragmentActivity, plainText: String, title: String, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
77
+ val requireAuth = title.isNotEmpty()
78
+ if (requireAuth) {
79
+ authenticate(activity, Cipher.ENCRYPT_MODE, null, title, { cipher ->
80
+ try {
81
+ val iv = cipher.iv
82
+ val encryptedData = cipher.doFinal(plainText.toByteArray(Charsets.UTF_8))
83
+
84
+ val combined = ByteArray(iv.size + encryptedData.size)
85
+ System.arraycopy(iv, 0, combined, 0, iv.size)
86
+ System.arraycopy(encryptedData, 0, combined, iv.size, encryptedData.size)
87
+
88
+ onSuccess(Base64.encodeToString(combined, Base64.DEFAULT))
89
+ } catch (e: Exception) {
90
+ onError("Encryption failed: ${e.message}")
91
+ }
92
+ }, onError)
93
+ } else {
94
+ try {
95
+ val cipher = getCipher()
96
+ val secretKey = getSecretKey(keyAliasNonBiometric)
97
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey)
98
+ val iv = cipher.iv
99
+ val encryptedData = cipher.doFinal(plainText.toByteArray(Charsets.UTF_8))
100
+
101
+ val combined = ByteArray(iv.size + encryptedData.size)
102
+ System.arraycopy(iv, 0, combined, 0, iv.size)
103
+ System.arraycopy(encryptedData, 0, combined, iv.size, encryptedData.size)
104
+
105
+ onSuccess(Base64.encodeToString(combined, Base64.DEFAULT))
106
+ } catch (e: Exception) {
107
+ onError("Encryption failed: ${e.message}")
108
+ }
109
+ }
110
+ }
111
+
112
+ fun decrypt(activity: FragmentActivity, encryptedBase64: String, title: String, onSuccess: (String) -> Unit, onError: (String) -> Unit) {
113
+ val requireAuth = title.isNotEmpty()
114
+ try {
115
+ val combined = Base64.decode(encryptedBase64, Base64.DEFAULT)
116
+ // GCM IV is 12 bytes
117
+ val iv = ByteArray(12)
118
+ val encryptedData = ByteArray(combined.size - 12)
119
+ System.arraycopy(combined, 0, iv, 0, 12)
120
+ System.arraycopy(combined, 12, encryptedData, 0, encryptedData.size)
121
+
122
+ if (requireAuth) {
123
+ authenticate(activity, Cipher.DECRYPT_MODE, iv, title, { cipher ->
124
+ try {
125
+ val decryptedData = cipher.doFinal(encryptedData)
126
+ onSuccess(String(decryptedData, Charsets.UTF_8))
127
+ } catch (e: Exception) {
128
+ onError("Decryption failed: ${e.message}")
129
+ }
130
+ }, onError)
131
+ } else {
132
+ try {
133
+ val cipher = getCipher()
134
+ val secretKey = getSecretKey(keyAliasNonBiometric)
135
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, iv))
136
+ val decryptedData = cipher.doFinal(encryptedData)
137
+ onSuccess(String(decryptedData, Charsets.UTF_8))
138
+ } catch (e: Exception) {
139
+ onError("Decryption failed: ${e.message}")
140
+ }
141
+ }
142
+ } catch (e: Exception) {
143
+ onError("Invalid data format")
144
+ }
145
+ }
146
+
147
+ private fun authenticate(activity: FragmentActivity, mode: Int, iv: ByteArray?, title: String, onSuccess: (Cipher) -> Unit, onError: (String) -> Unit) {
148
+ activity.runOnUiThread {
149
+ val executor = ContextCompat.getMainExecutor(context)
150
+ val biometricPrompt = BiometricPrompt(activity, executor,
151
+ object : BiometricPrompt.AuthenticationCallback() {
152
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
153
+ super.onAuthenticationError(errorCode, errString)
154
+ onError("Authentication error: $errString")
155
+ }
156
+
157
+ override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
158
+ super.onAuthenticationSucceeded(result)
159
+ result.cryptoObject?.cipher?.let {
160
+ onSuccess(it)
161
+ } ?: onError("Cipher not initialized")
162
+ }
163
+
164
+ override fun onAuthenticationFailed() {
165
+ super.onAuthenticationFailed()
166
+ onError("Authentication failed")
167
+ }
168
+ })
169
+
170
+ val promptInfo = BiometricPrompt.PromptInfo.Builder()
171
+ .setTitle(title)
172
+ .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
173
+ .build()
174
+
175
+ try {
176
+ val cipher = getCipher()
177
+ val secretKey = getSecretKey(keyAliasBiometric)
178
+ if (mode == Cipher.ENCRYPT_MODE) {
179
+ cipher.init(mode, secretKey)
180
+ } else {
181
+ if (iv == null) throw IllegalArgumentException("IV required for decryption")
182
+ cipher.init(mode, secretKey, GCMParameterSpec(128, iv))
183
+ }
184
+ biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
185
+ } catch (e: Exception) {
186
+ onError("Crypto initialization failed: ${e.message}")
187
+ }
188
+ }
189
+ }
190
+ }
@@ -0,0 +1,103 @@
1
+ package com.hituchhimpa.reactnativeauthvault
2
+
3
+ import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.WritableMap
6
+ import com.facebook.react.bridge.Arguments
7
+ import androidx.fragment.app.FragmentActivity
8
+
9
+ class ReactNativeAuthVaultModule(reactContext: ReactApplicationContext) :
10
+ NativeReactNativeAuthVaultSpec(reactContext) {
11
+
12
+ private val cryptoEngine = CryptoEngine(reactContext)
13
+
14
+ override fun audit(): WritableMap {
15
+ val map = Arguments.createMap()
16
+ val auditResult = SecurityEngine.audit(reactApplicationContext)
17
+
18
+ for ((key, value) in auditResult) {
19
+ when (value) {
20
+ is Boolean -> map.putBoolean(key, value)
21
+ is Int -> map.putInt(key, value)
22
+ is Double -> map.putDouble(key, value)
23
+ is String -> map.putString(key, value)
24
+ }
25
+ }
26
+
27
+ return map
28
+ }
29
+
30
+ override fun encrypt(plainText: String, prompt: String, promise: Promise) {
31
+ val activity = currentActivity as? FragmentActivity
32
+ if (activity == null) {
33
+ promise.reject("ERR_ACTIVITY", "Activity is null or not a FragmentActivity")
34
+ return
35
+ }
36
+
37
+ cryptoEngine.encrypt(activity, plainText, prompt,
38
+ onSuccess = { encrypted -> promise.resolve(encrypted) },
39
+ onError = { error -> promise.reject("ERR_ENCRYPT", error) }
40
+ )
41
+ }
42
+
43
+ override fun decrypt(encryptedBase64: String, prompt: String, promise: Promise) {
44
+ val activity = currentActivity as? FragmentActivity
45
+ if (activity == null) {
46
+ promise.reject("ERR_ACTIVITY", "Activity is null or not a FragmentActivity")
47
+ return
48
+ }
49
+
50
+ cryptoEngine.decrypt(activity, encryptedBase64, prompt,
51
+ onSuccess = { decrypted -> promise.resolve(decrypted) },
52
+ onError = { error -> promise.reject("ERR_DECRYPT", error) }
53
+ )
54
+ }
55
+
56
+ private fun getSharedPreferences(): android.content.SharedPreferences {
57
+ return reactApplicationContext.getSharedPreferences("ReactNativeAuthVaultStorage", android.content.Context.MODE_PRIVATE)
58
+ }
59
+
60
+ override fun setItem(key: String, value: String, prompt: String, promise: Promise) {
61
+ val activity = currentActivity as? FragmentActivity
62
+ if (activity == null) {
63
+ promise.reject("ERR_ACTIVITY", "Activity is null or not a FragmentActivity")
64
+ return
65
+ }
66
+
67
+ cryptoEngine.encrypt(activity, value, prompt,
68
+ onSuccess = { encrypted ->
69
+ getSharedPreferences().edit().putString(key, encrypted).apply()
70
+ promise.resolve(true)
71
+ },
72
+ onError = { error -> promise.reject("ERR_ENCRYPT", error) }
73
+ )
74
+ }
75
+
76
+ override fun getItem(key: String, prompt: String, promise: Promise) {
77
+ val encrypted = getSharedPreferences().getString(key, null)
78
+ if (encrypted == null) {
79
+ promise.resolve(null)
80
+ return
81
+ }
82
+
83
+ val activity = currentActivity as? FragmentActivity
84
+ if (activity == null) {
85
+ promise.reject("ERR_ACTIVITY", "Activity is null or not a FragmentActivity")
86
+ return
87
+ }
88
+
89
+ cryptoEngine.decrypt(activity, encrypted, prompt,
90
+ onSuccess = { decrypted -> promise.resolve(decrypted) },
91
+ onError = { error -> promise.reject("ERR_DECRYPT", error) }
92
+ )
93
+ }
94
+
95
+ override fun removeItem(key: String, promise: Promise) {
96
+ getSharedPreferences().edit().remove(key).apply()
97
+ promise.resolve(true)
98
+ }
99
+
100
+ companion object {
101
+ const val NAME = NativeReactNativeAuthVaultSpec.NAME
102
+ }
103
+ }
@@ -0,0 +1,31 @@
1
+ package com.hituchhimpa.reactnativeauthvault
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+ import java.util.HashMap
9
+
10
+ class ReactNativeAuthVaultPackage : BaseReactPackage() {
11
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
+ return if (name == ReactNativeAuthVaultModule.NAME) {
13
+ ReactNativeAuthVaultModule(reactContext)
14
+ } else {
15
+ null
16
+ }
17
+ }
18
+
19
+ override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
20
+ mapOf(
21
+ ReactNativeAuthVaultModule.NAME to ReactModuleInfo(
22
+ name = ReactNativeAuthVaultModule.NAME,
23
+ className = ReactNativeAuthVaultModule.NAME,
24
+ canOverrideExistingModule = false,
25
+ needsEagerInit = false,
26
+ isCxxModule = false,
27
+ isTurboModule = true
28
+ )
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,75 @@
1
+ package com.hituchhimpa.reactnativeauthvault
2
+
3
+ import android.os.Build
4
+ import android.os.Debug
5
+ import java.io.File
6
+
7
+ object SecurityEngine {
8
+
9
+ fun audit(context: android.content.Context): Map<String, Any> {
10
+ val rooted = isRooted()
11
+ val emulator = isEmulator()
12
+ val debugger = isDebuggerAttached()
13
+
14
+ val hardwareBacked = context.packageManager.hasSystemFeature("android.hardware.strongbox_keystore")
15
+ val biometricEnabled = androidx.biometric.BiometricManager.from(context).canAuthenticate(androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG) == androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS
16
+
17
+ var score = 100
18
+ if (rooted) score -= 50
19
+ if (emulator) score -= 20
20
+ if (debugger) score -= 20
21
+ if (!hardwareBacked) score -= 10
22
+ if (!biometricEnabled) score -= 5
23
+
24
+ return mapOf(
25
+ "secureStorage" to true,
26
+ "hardwareBacked" to hardwareBacked,
27
+ "biometricEnabled" to biometricEnabled,
28
+ "rooted" to rooted,
29
+ "jailbroken" to false,
30
+ "emulator" to emulator,
31
+ "debuggerAttached" to debugger,
32
+ "securityScore" to score
33
+ )
34
+ }
35
+
36
+ private fun isRooted(): Boolean {
37
+ val buildTags = Build.TAGS
38
+ if (buildTags != null && buildTags.contains("test-keys")) {
39
+ return true
40
+ }
41
+
42
+ val paths = arrayOf(
43
+ "/system/app/Superuser.apk",
44
+ "/sbin/su",
45
+ "/system/bin/su",
46
+ "/system/xbin/su",
47
+ "/data/local/xbin/su",
48
+ "/data/local/bin/su",
49
+ "/system/sd/xbin/su",
50
+ "/system/bin/failsafe/su",
51
+ "/data/local/su",
52
+ "/su/bin/su"
53
+ )
54
+ for (path in paths) {
55
+ if (File(path).exists()) return true
56
+ }
57
+
58
+ return false
59
+ }
60
+
61
+ private fun isEmulator(): Boolean {
62
+ return (Build.FINGERPRINT.startsWith("generic")
63
+ || Build.FINGERPRINT.startsWith("unknown")
64
+ || Build.MODEL.contains("google_sdk")
65
+ || Build.MODEL.contains("Emulator")
66
+ || Build.MODEL.contains("Android SDK built for x86")
67
+ || Build.MANUFACTURER.contains("Genymotion")
68
+ || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
69
+ || "google_sdk" == Build.PRODUCT)
70
+ }
71
+
72
+ private fun isDebuggerAttached(): Boolean {
73
+ return Debug.isDebuggerConnected() || Debug.waitingForDebugger()
74
+ }
75
+ }
@@ -0,0 +1,160 @@
1
+ import Foundation
2
+ import LocalAuthentication
3
+ import Security
4
+
5
+ @objc(CryptoEngine)
6
+ public class CryptoEngine: NSObject {
7
+ private let keyTagBiometric = "com.hituchhimpa.reactnativeauthvault.key.biometric".data(using: .utf8)!
8
+ private let keyTagNonBiometric = "com.hituchhimpa.reactnativeauthvault.key.nonbiometric".data(using: .utf8)!
9
+
10
+ @objc
11
+ public func initializeKey() {
12
+ if getPublicKey(useBiometric: true) == nil {
13
+ try? generateKey(useBiometric: true)
14
+ }
15
+ if getPublicKey(useBiometric: false) == nil {
16
+ try? generateKey(useBiometric: false)
17
+ }
18
+ }
19
+
20
+ private func generateKey(useBiometric: Bool) throws {
21
+ var error: Unmanaged<CFError>?
22
+
23
+ let access: SecAccessControl
24
+ if useBiometric {
25
+ // Require FaceID / TouchID for private key usage
26
+ guard let acc = SecAccessControlCreateWithFlags(
27
+ kCFAllocatorDefault,
28
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
29
+ [.userPresence, .privateKeyUsage],
30
+ &error
31
+ ) else {
32
+ throw error!.takeRetainedValue() as Error
33
+ }
34
+ access = acc
35
+ } else {
36
+ guard let acc = SecAccessControlCreateWithFlags(
37
+ kCFAllocatorDefault,
38
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
39
+ [.privateKeyUsage],
40
+ &error
41
+ ) else {
42
+ throw error!.takeRetainedValue() as Error
43
+ }
44
+ access = acc
45
+ }
46
+
47
+ let tag = useBiometric ? keyTagBiometric : keyTagNonBiometric
48
+ let attributes: [String: Any] = [
49
+ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
50
+ kSecAttrKeySizeInBits as String: 256,
51
+ kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
52
+ kSecPrivateKeyAttrs as String: [
53
+ kSecAttrIsPermanent as String: true,
54
+ kSecAttrApplicationTag as String: tag,
55
+ kSecAttrAccessControl as String: access
56
+ ]
57
+ ]
58
+
59
+ let status = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
60
+ if status == nil, let error = error {
61
+ throw error.takeRetainedValue() as Error
62
+ }
63
+ }
64
+
65
+ private func getKey(useBiometric: Bool, prompt: String = "Authenticate to access key") -> SecKey? {
66
+ let tag = useBiometric ? keyTagBiometric : keyTagNonBiometric
67
+ var query: [String: Any] = [
68
+ kSecClass as String: kSecClassKey,
69
+ kSecAttrApplicationTag as String: tag,
70
+ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
71
+ kSecReturnRef as String: true
72
+ ]
73
+ if useBiometric {
74
+ query[kSecUseOperationPrompt as String] = prompt
75
+ }
76
+
77
+ var item: CFTypeRef?
78
+ let status = SecItemCopyMatching(query as CFDictionary, &item)
79
+ if status == errSecSuccess {
80
+ return (item as! SecKey)
81
+ }
82
+ return nil
83
+ }
84
+
85
+ @objc
86
+ public func encrypt(plainText: String, prompt: String, completion: @escaping (String?, String?) -> Void) {
87
+ let useBiometric = !prompt.isEmpty
88
+ DispatchQueue.global(qos: .userInitiated).async {
89
+ guard let publicKey = self.getPublicKey(useBiometric: useBiometric) else {
90
+ completion(nil, "Key not found")
91
+ return
92
+ }
93
+
94
+ guard let data = plainText.data(using: .utf8) else {
95
+ completion(nil, "Invalid string")
96
+ return
97
+ }
98
+
99
+ var error: Unmanaged<CFError>?
100
+ guard let encryptedData = SecKeyCreateEncryptedData(
101
+ publicKey,
102
+ .eciesEncryptionStandardX963SHA256AESGCM,
103
+ data as CFData,
104
+ &error
105
+ ) as Data? else {
106
+ completion(nil, error?.takeRetainedValue().localizedDescription ?? "Encryption failed")
107
+ return
108
+ }
109
+
110
+ completion(encryptedData.base64EncodedString(), nil)
111
+ }
112
+ }
113
+
114
+ @objc
115
+ public func decrypt(encryptedBase64: String, prompt: String, completion: @escaping (String?, String?) -> Void) {
116
+ let useBiometric = !prompt.isEmpty
117
+ DispatchQueue.global(qos: .userInitiated).async {
118
+ guard let privateKey = self.getKey(useBiometric: useBiometric, prompt: prompt) else {
119
+ completion(nil, "Authentication failed or key not found")
120
+ return
121
+ }
122
+
123
+ guard let data = Data(base64Encoded: encryptedBase64) else {
124
+ completion(nil, "Invalid base64")
125
+ return
126
+ }
127
+
128
+ var error: Unmanaged<CFError>?
129
+ guard let decryptedData = SecKeyCreateDecryptedData(
130
+ privateKey,
131
+ .eciesEncryptionStandardX963SHA256AESGCM,
132
+ data as CFData,
133
+ &error
134
+ ) as Data? else {
135
+ completion(nil, error?.takeRetainedValue().localizedDescription ?? "Decryption failed")
136
+ return
137
+ }
138
+
139
+ completion(String(data: decryptedData, encoding: .utf8), nil)
140
+ }
141
+ }
142
+
143
+ private func getPublicKey(useBiometric: Bool) -> SecKey? {
144
+ let tag = useBiometric ? keyTagBiometric : keyTagNonBiometric
145
+ let query: [String: Any] = [
146
+ kSecClass as String: kSecClassKey,
147
+ kSecAttrApplicationTag as String: tag,
148
+ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
149
+ kSecReturnRef as String: true
150
+ ]
151
+
152
+ var item: CFTypeRef?
153
+ let status = SecItemCopyMatching(query as CFDictionary, &item)
154
+ if status == errSecSuccess {
155
+ let privateKey = item as! SecKey
156
+ return SecKeyCopyPublicKey(privateKey)
157
+ }
158
+ return nil
159
+ }
160
+ }
@@ -0,0 +1,5 @@
1
+ #import <ReactNativeAuthVaultSpec/ReactNativeAuthVaultSpec.h>
2
+
3
+ @interface ReactNativeAuthVault : NSObject <NativeReactNativeAuthVaultSpec>
4
+
5
+ @end
@@ -0,0 +1,89 @@
1
+ #import "ReactNativeAuthVault.h"
2
+
3
+ #if __has_include("react_native_auth_vault/react_native_auth_vault-Swift.h")
4
+ #import "react_native_auth_vault/react_native_auth_vault-Swift.h"
5
+ #else
6
+ #import "react_native_auth_vault-Swift.h"
7
+ #endif
8
+
9
+ @implementation ReactNativeAuthVault {
10
+ CryptoEngine *_cryptoEngine;
11
+ }
12
+
13
+ - (instancetype)init {
14
+ self = [super init];
15
+ if (self) {
16
+ _cryptoEngine = [[CryptoEngine alloc] init];
17
+ [_cryptoEngine initializeKey];
18
+ }
19
+ return self;
20
+ }
21
+
22
+ - (NSDictionary *)audit {
23
+ return [SecurityEngine audit];
24
+ }
25
+
26
+ - (void)encrypt:(NSString *)plainText prompt:(NSString *)prompt resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
27
+ [_cryptoEngine encryptWithPlainText:plainText prompt:prompt completion:^(NSString * _Nullable encryptedBase64, NSString * _Nullable error) {
28
+ if (error) {
29
+ reject(@"ERR_ENCRYPT", error, nil);
30
+ } else {
31
+ resolve(encryptedBase64);
32
+ }
33
+ }];
34
+ }
35
+
36
+ - (void)decrypt:(NSString *)encryptedBase64 prompt:(NSString *)prompt resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
37
+ [_cryptoEngine decryptWithEncryptedBase64:encryptedBase64 prompt:prompt completion:^(NSString * _Nullable decryptedText, NSString * _Nullable error) {
38
+ if (error) {
39
+ reject(@"ERR_DECRYPT", error, nil);
40
+ } else {
41
+ resolve(decryptedText);
42
+ }
43
+ }];
44
+ }
45
+
46
+ - (void)setItem:(NSString *)key value:(NSString *)value prompt:(NSString *)prompt resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
47
+ [_cryptoEngine encryptWithPlainText:value prompt:prompt completion:^(NSString * _Nullable encryptedBase64, NSString * _Nullable error) {
48
+ if (error) {
49
+ reject(@"ERR_ENCRYPT", error, nil);
50
+ } else {
51
+ [[NSUserDefaults standardUserDefaults] setObject:encryptedBase64 forKey:key];
52
+ resolve(@(YES));
53
+ }
54
+ }];
55
+ }
56
+
57
+ - (void)getItem:(NSString *)key prompt:(NSString *)prompt resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
58
+ NSString *encryptedBase64 = [[NSUserDefaults standardUserDefaults] stringForKey:key];
59
+ if (!encryptedBase64) {
60
+ resolve([NSNull null]);
61
+ return;
62
+ }
63
+
64
+ [_cryptoEngine decryptWithEncryptedBase64:encryptedBase64 prompt:prompt completion:^(NSString * _Nullable decryptedText, NSString * _Nullable error) {
65
+ if (error) {
66
+ reject(@"ERR_DECRYPT", error, nil);
67
+ } else {
68
+ resolve(decryptedText);
69
+ }
70
+ }];
71
+ }
72
+
73
+ - (void)removeItem:(NSString *)key resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
74
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
75
+ resolve(@(YES));
76
+ }
77
+
78
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
79
+ (const facebook::react::ObjCTurboModule::InitParams &)params
80
+ {
81
+ return std::make_shared<facebook::react::NativeReactNativeAuthVaultSpecJSI>(params);
82
+ }
83
+
84
+ + (NSString *)moduleName
85
+ {
86
+ return @"ReactNativeAuthVault";
87
+ }
88
+
89
+ @end
@@ -0,0 +1,85 @@
1
+ import Foundation
2
+ import UIKit
3
+ import Darwin
4
+
5
+ @objc(SecurityEngine)
6
+ public class SecurityEngine: NSObject {
7
+
8
+ @objc
9
+ public static func audit() -> [String: Any] {
10
+ let jailbroken = isJailbroken()
11
+ let emulator = isEmulator()
12
+ let debugger = isDebuggerAttached()
13
+
14
+ let context = LAContext()
15
+ var error: NSError?
16
+ let biometricEnabled = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
17
+
18
+ var score = 100
19
+ if jailbroken { score -= 50 }
20
+ if emulator { score -= 20 }
21
+ if debugger { score -= 20 }
22
+ if !biometricEnabled { score -= 5 }
23
+
24
+ return [
25
+ "secureStorage": true,
26
+ "hardwareBacked": true,
27
+ "biometricEnabled": biometricEnabled,
28
+ "rooted": false,
29
+ "jailbroken": jailbroken,
30
+ "emulator": emulator,
31
+ "debuggerAttached": debugger,
32
+ "securityScore": score
33
+ ]
34
+ }
35
+
36
+ private static func isJailbroken() -> Bool {
37
+ #if targetEnvironment(simulator)
38
+ return false
39
+ #else
40
+ let paths = [
41
+ "/Applications/Cydia.app",
42
+ "/Library/MobileSubstrate/MobileSubstrate.dylib",
43
+ "/bin/bash",
44
+ "/usr/sbin/sshd",
45
+ "/etc/apt",
46
+ "/usr/bin/ssh"
47
+ ]
48
+
49
+ for path in paths {
50
+ if FileManager.default.fileExists(atPath: path) {
51
+ return true
52
+ }
53
+ }
54
+
55
+ // Try writing to a restricted area
56
+ do {
57
+ let path = "/private/jailbreak_test.txt"
58
+ try "test".write(toFile: path, atomically: true, encoding: .utf8)
59
+ try FileManager.default.removeItem(atPath: path)
60
+ return true
61
+ } catch {
62
+ return false
63
+ }
64
+ #endif
65
+ }
66
+
67
+ private static func isEmulator() -> Bool {
68
+ #if targetEnvironment(simulator)
69
+ return true
70
+ #else
71
+ return false
72
+ #endif
73
+ }
74
+
75
+ private static func isDebuggerAttached() -> Bool {
76
+ var info = kinfo_proc()
77
+ var mib : [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
78
+ var size = MemoryLayout<kinfo_proc>.stride
79
+ let junk = sysctl(&mib, UInt32(mib.count), &info, &size, nil, 0)
80
+ if junk != 0 {
81
+ return false
82
+ }
83
+ return (info.kp_proc.p_flag & P_TRACED) != 0
84
+ }
85
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ export default TurboModuleRegistry.getEnforcing('ReactNativeAuthVault');
5
+ //# sourceMappingURL=NativeReactNativeAuthVault.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeReactNativeAuthVault.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAWpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,sBAAsB,CAAC","ignoreList":[]}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ import ReactNativeAuthVault from "./NativeReactNativeAuthVault.js";
4
+ export const AuthVault = {
5
+ audit: () => ReactNativeAuthVault.audit(),
6
+ encrypt: (plainText, prompt) => ReactNativeAuthVault.encrypt(plainText, prompt),
7
+ decrypt: (encryptedBase64, prompt) => ReactNativeAuthVault.decrypt(encryptedBase64, prompt),
8
+ setItem: (key, value, prompt) => ReactNativeAuthVault.setItem(key, value, prompt),
9
+ getItem: (key, prompt) => ReactNativeAuthVault.getItem(key, prompt),
10
+ removeItem: key => ReactNativeAuthVault.removeItem(key)
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["ReactNativeAuthVault","AuthVault","audit","encrypt","plainText","prompt","decrypt","encryptedBase64","setItem","key","value","getItem","removeItem"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,oBAAoB,MAAM,iCAA8B;AAE/D,OAAO,MAAMC,SAAS,GAAG;EACvBC,KAAK,EAAEA,CAAA,KAAcF,oBAAoB,CAACE,KAAK,CAAC,CAAC;EACjDC,OAAO,EAAEA,CAACC,SAAiB,EAAEC,MAAc,KAAsBL,oBAAoB,CAACG,OAAO,CAACC,SAAS,EAAEC,MAAM,CAAC;EAChHC,OAAO,EAAEA,CAACC,eAAuB,EAAEF,MAAc,KAAsBL,oBAAoB,CAACM,OAAO,CAACC,eAAe,EAAEF,MAAM,CAAC;EAC5HG,OAAO,EAAEA,CAACC,GAAW,EAAEC,KAAa,EAAEL,MAAc,KAAuBL,oBAAoB,CAACQ,OAAO,CAACC,GAAG,EAAEC,KAAK,EAAEL,MAAM,CAAC;EAC3HM,OAAO,EAAEA,CAACF,GAAW,EAAEJ,MAAc,KAA6BL,oBAAoB,CAACW,OAAO,CAACF,GAAG,EAAEJ,MAAM,CAAC;EAC3GO,UAAU,EAAGH,GAAW,IAAuBT,oBAAoB,CAACY,UAAU,CAACH,GAAG;AACpF,CAAC","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,12 @@
1
+ import { type TurboModule } from 'react-native';
2
+ export interface Spec extends TurboModule {
3
+ audit(): Object;
4
+ encrypt(plainText: string, prompt: string): Promise<string>;
5
+ decrypt(encryptedBase64: string, prompt: string): Promise<string>;
6
+ setItem(key: string, value: string, prompt: string): Promise<boolean>;
7
+ getItem(key: string, prompt: string): Promise<string | null>;
8
+ removeItem(key: string): Promise<boolean>;
9
+ }
10
+ declare const _default: Spec;
11
+ export default _default;
12
+ //# sourceMappingURL=NativeReactNativeAuthVault.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeReactNativeAuthVault.d.ts","sourceRoot":"","sources":["../../../src/NativeReactNativeAuthVault.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,KAAK,IAAI,MAAM,CAAC;IAChB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,OAAO,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3C;;AAED,wBAA8E"}
@@ -0,0 +1,9 @@
1
+ export declare const AuthVault: {
2
+ audit: () => Object;
3
+ encrypt: (plainText: string, prompt: string) => Promise<string>;
4
+ decrypt: (encryptedBase64: string, prompt: string) => Promise<string>;
5
+ setItem: (key: string, value: string, prompt: string) => Promise<boolean>;
6
+ getItem: (key: string, prompt: string) => Promise<string | null>;
7
+ removeItem: (key: string) => Promise<boolean>;
8
+ };
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;iBACT,MAAM;yBACI,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;+BAClC,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;mBACpD,MAAM,SAAS,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;mBACxD,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;sBAC5C,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;CAC5C,CAAC"}
package/package.json ADDED
@@ -0,0 +1,137 @@
1
+ {
2
+ "name": "@hituchhimpa/react-native-auth-vault",
3
+ "version": "0.1.0",
4
+ "description": "Native-first React Native security and authentication library",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./lib/typescript/src/index.d.ts",
10
+ "default": "./lib/module/index.js"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "lib",
16
+ "android",
17
+ "ios",
18
+ "cpp",
19
+ "*.podspec",
20
+ "react-native.config.js",
21
+ "!ios/build",
22
+ "!android/build",
23
+ "!android/gradle",
24
+ "!android/gradlew",
25
+ "!android/gradlew.bat",
26
+ "!android/local.properties",
27
+ "!**/__tests__",
28
+ "!**/__fixtures__",
29
+ "!**/__mocks__",
30
+ "!**/.*"
31
+ ],
32
+ "scripts": {
33
+ "example": "yarn workspace @hituchhimpa/react-native-auth-vault-example",
34
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
35
+ "prepare": "bob build",
36
+ "typecheck": "tsc",
37
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
38
+ "test": "jest"
39
+ },
40
+ "keywords": [
41
+ "react-native",
42
+ "ios",
43
+ "android"
44
+ ],
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/hituchhimpa7/react-native-auth-vault.git.git"
48
+ },
49
+ "author": "Hitesh Chhimpa <hituchhimpa7@users.noreply.github.com> (https://github.com/hituchhimpa7)",
50
+ "license": "MIT",
51
+ "bugs": {
52
+ "url": "https://github.com/hituchhimpa7/react-native-auth-vault.git/issues"
53
+ },
54
+ "homepage": "https://github.com/hituchhimpa7/react-native-auth-vault.git#readme",
55
+ "publishConfig": {
56
+ "registry": "https://registry.npmjs.org/"
57
+ },
58
+ "devDependencies": {
59
+ "@eslint/compat": "^2.0.3",
60
+ "@eslint/eslintrc": "^3.3.5",
61
+ "@eslint/js": "^10.0.1",
62
+ "@jest/globals": "^30.0.0",
63
+ "@react-native/babel-preset": "0.85.0",
64
+ "@react-native/eslint-config": "0.85.0",
65
+ "@react-native/jest-preset": "0.85.0",
66
+ "@types/react": "^19.2.0",
67
+ "del-cli": "^7.0.0",
68
+ "eslint": "^9.39.4",
69
+ "eslint-config-prettier": "^10.1.8",
70
+ "eslint-plugin-ft-flow": "^3.0.11",
71
+ "eslint-plugin-prettier": "^5.5.5",
72
+ "jest": "^30.3.0",
73
+ "prettier": "^3.8.1",
74
+ "react": "19.2.3",
75
+ "react-native": "0.85.0",
76
+ "react-native-builder-bob": "^0.41.0",
77
+ "turbo": "^2.8.21",
78
+ "typescript": "^6.0.2"
79
+ },
80
+ "peerDependencies": {
81
+ "react": "*",
82
+ "react-native": "*"
83
+ },
84
+ "workspaces": [
85
+ "example"
86
+ ],
87
+ "packageManager": "yarn@4.11.0",
88
+ "react-native-builder-bob": {
89
+ "source": "src",
90
+ "output": "lib",
91
+ "targets": [
92
+ [
93
+ "module",
94
+ {
95
+ "esm": true
96
+ }
97
+ ],
98
+ [
99
+ "typescript",
100
+ {
101
+ "project": "tsconfig.build.json"
102
+ }
103
+ ]
104
+ ]
105
+ },
106
+ "codegenConfig": {
107
+ "name": "ReactNativeAuthVaultSpec",
108
+ "type": "modules",
109
+ "jsSrcsDir": "src",
110
+ "android": {
111
+ "javaPackageName": "com.hituchhimpa.reactnativeauthvault"
112
+ }
113
+ },
114
+ "prettier": {
115
+ "quoteProps": "consistent",
116
+ "singleQuote": true,
117
+ "tabWidth": 2,
118
+ "trailingComma": "es5",
119
+ "useTabs": false
120
+ },
121
+ "jest": {
122
+ "preset": "@react-native/jest-preset",
123
+ "modulePathIgnorePatterns": [
124
+ "<rootDir>/example/node_modules",
125
+ "<rootDir>/lib/"
126
+ ]
127
+ },
128
+ "create-react-native-library": {
129
+ "type": "turbo-module",
130
+ "languages": "kotlin-objc",
131
+ "tools": [
132
+ "eslint",
133
+ "jest"
134
+ ],
135
+ "version": "0.62.0"
136
+ }
137
+ }