Skip to content

Commit 0c7796a

Browse files
authored
Merge pull request #532 from mCodex/feat/androidBiometric14
Feat/android biometric14
2 parents b84ec82 + 5bdbb63 commit 0c7796a

File tree

10 files changed

+1038
-985
lines changed

10 files changed

+1038
-985
lines changed

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ dependencies {
138138
// Add a dependency on NitroModules
139139
implementation project(":react-native-nitro-modules")
140140

141-
implementation "androidx.biometric:biometric:1.1.0"
141+
implementation "androidx.biometric:biometric-ktx:1.4.0-alpha02"
142142
}
143143

144144
if (isNewArchitectureEnabled()) {

android/src/main/java/com/sensitiveinfo/HybridSensitiveInfo.kt

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.sensitiveinfo
22

3+
import androidx.annotation.Keep
4+
import com.facebook.proguard.annotations.DoNotStrip
5+
36
import android.content.Context
47
import com.margelo.nitro.core.Promise
58
import com.margelo.nitro.sensitiveinfo.*
@@ -24,6 +27,8 @@ import kotlin.jvm.Volatile
2427
* This class provides secure storage for sensitive data on Android using the Android Keystore
2528
* for key management and SharedPreferences for encrypted data persistence.
2629
*/
30+
@DoNotStrip
31+
@Keep
2732
class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
2833
private data class Dependencies(
2934
val context: Context,
@@ -98,14 +103,33 @@ class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
98103
}
99104
}
100105

101-
override fun getItem(request: SensitiveInfoGetRequest): Promise<SensitiveInfoItem?> {
106+
override fun getItem(request: SensitiveInfoGetRequest): Promise<Variant_NullType_SensitiveInfoItem> {
102107
return Promise.async(coroutineScope) {
103108
val deps = ensureInitialized()
104109
val service = deps.serviceNameResolver.resolve(request.service)
105110

106111
val entry = deps.storage.read(service, request.key)
112+
107113
if (entry == null) {
108-
return@async null
114+
try {
115+
val ctor = com.margelo.nitro.core.NullType::class.java.getDeclaredConstructor()
116+
ctor.isAccessible = true
117+
val nullTypeInstance = ctor.newInstance()
118+
return@async Variant_NullType_SensitiveInfoItem.create(nullTypeInstance)
119+
} catch (e: Throwable) {
120+
// Fallback: create a null-type via unsafe camino — return a Second with empty SensitiveInfoItem omitted
121+
return@async Variant_NullType_SensitiveInfoItem.create(com.margelo.nitro.sensitiveinfo.SensitiveInfoItem(
122+
key = request.key,
123+
service = service,
124+
value = null,
125+
metadata = StorageMetadata(
126+
securityLevel = SecurityLevel.SOFTWARE,
127+
backend = StorageBackend.ANDROIDKEYSTORE,
128+
accessControl = AccessControl.NONE,
129+
timestamp = System.currentTimeMillis() / 1000.0
130+
)
131+
))
132+
}
109133
}
110134

111135
val metadata = entry.metadata.toStorageMetadata()
@@ -131,15 +155,17 @@ class HybridSensitiveInfo : HybridSensitiveInfoSpec() {
131155
null
132156
}
133157

134-
SensitiveInfoItem(
135-
key = request.key,
136-
service = service,
137-
value = value,
138-
metadata = metadata ?: StorageMetadata(
139-
securityLevel = SecurityLevel.SOFTWARE,
140-
backend = StorageBackend.ANDROIDKEYSTORE,
141-
accessControl = AccessControl.NONE,
142-
timestamp = System.currentTimeMillis() / 1000.0
158+
Variant_NullType_SensitiveInfoItem.create(
159+
SensitiveInfoItem(
160+
key = request.key,
161+
service = service,
162+
value = value,
163+
metadata = metadata ?: StorageMetadata(
164+
securityLevel = SecurityLevel.SOFTWARE,
165+
backend = StorageBackend.ANDROIDKEYSTORE,
166+
accessControl = AccessControl.NONE,
167+
timestamp = System.currentTimeMillis() / 1000.0
168+
)
143169
)
144170
)
145171
}

android/src/main/java/com/sensitiveinfo/internal/auth/BiometricAuthenticator.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,22 @@ internal class BiometricAuthenticator {
120120
val builder = BiometricPrompt.PromptInfo.Builder()
121121
.setTitle(prompt.title)
122122

123+
// Prefer disabling confirmation on supported devices to streamline UX while maintaining
124+
// biometric security. Newer Biometric APIs support `setConfirmationRequired`.
125+
try {
126+
builder.setConfirmationRequired(false)
127+
} catch (_: Throwable) {
128+
// Ignore if the platform/library doesn't support this method.
129+
}
130+
123131
prompt.subtitle?.let(builder::setSubtitle)
124132
prompt.description?.let(builder::setDescription)
125133

126134
var promptAuthenticators = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
135+
// Newer biometric library versions (1.4.x+) prefer `setAllowedAuthenticators`.
127136
allowedAuthenticators
128137
} else {
138+
// On older platforms fall back to the legacy flags.
129139
allowedAuthenticators and (BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
130140
}
131141

example/android/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
1515
# When configured, Gradle will run in incubating parallel mode.
1616
# This option should only be used with decoupled projects. More details, visit
1717
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18-
# org.gradle.parallel=true
18+
org.gradle.parallel=true
1919

2020
# AndroidX package structure to make it clearer which packages are bundled with the
2121
# Android operating system, and which are packaged with your app's APK

0 commit comments

Comments
 (0)