diff --git a/patches/expo-secure-store/details.md b/patches/expo-secure-store/details.md
index e9ef686113ea..d6d176d012a1 100644
--- a/patches/expo-secure-store/details.md
+++ b/patches/expo-secure-store/details.md
@@ -1,22 +1,76 @@
 # `expo-secure-store` patches
 
-### [expo-secure-store+14.2.4+001+additional-config-options.patch](expo-secure-store+14.2.4+001+additional-config-options.patch)
+### [expo-secure-store+14.2.4+001+enable-device-fallback.patch](expo-secure-store+14.2.4+001+enable-device-fallback.patch)
 
 - Reason:
 
     ```
-    Expo SecureStore is a great library, but it lacks some of the functionality we need, i.e.:
+    We need to enable users to use any device screen lock instead of biometrics.
+    This is not enabled in the SecureStore, so this patch changes the required input to either biometrics or screen lock knowledge if the 'enableDeviceFallback' flag is set to true.
+    Additionally, support for screen locks can be checked using the new 'canUseDeviceCredentialsAuthentication' method. 
+    ```
+
+- Upstream PR/issue: https://github.com/expo/expo/pull/41409
+- E/App issue: https://github.com/Expensify/App/issues/75225
+- PR introducing patch: https://github.com/Expensify/App/pull/76288
+
+### [expo-secure-store+14.2.4+002+return-auth-type.patch](expo-secure-store+14.2.4+002+return-auth-type.patch)
+
+- Reason:
+
+    ```
+    This patch adds the `returnUsedAuthenticationType` flag.
+    When this flag is set to true, the get methods of SecureStore will return a two-element array. 
+    The first value will be the original value returned when this flag is set to false.
+    The second value is the authentication type used to read the value from the AUTH_TYPE object.
+    As for the set function, the returned value will simply be AUTH_TYPE.
+    It uses a pre-defined constant that mimics an enum and can also be imported directly from the app.
+    ```
+
+- Upstream PR/issue: TBA
+- E/App issue: https://github.com/Expensify/App/issues/75225
+- PR introducing patch: https://github.com/Expensify/App/pull/76288
+
+### [expo-secure-store+14.2.4+003+force-authentication-on-save.patch](expo-secure-store+14.2.4+003+force-authentication-on-save.patch)
+
+- Reason:
+
+    ```
+    The iOS does not require authentication when a value is saved to the keychain. We cannot force iOS to do so.
+    However, to maintain consistency with Android, setting the 'forceAuthenticationOnSave' flag to true results in a prompt appearing
+    before the value is saved. This only works in the asynchronous version of the save method.
+    ```
 
-    - Returning the type of authentication used for accessing the store
-    - Allowing the fallback to device credentials
-    - Enabling use of device credentials if no biometrics are enrolled/supported.
-    - Enforcing iOS to always ask for the authentication when accessing the store (by default iOS skips the authentication if, for example, user unlocked the screen recently with biometrics)
-    - Letting user know if the key entry is already stored instead of overwriting it without any feedback
+- Upstream PR/issue: TBA
+- E/App issue: https://github.com/Expensify/App/issues/75225
+- PR introducing patch: https://github.com/Expensify/App/pull/76288
 
+### [expo-secure-store+14.2.4+004+fail-on-update.patch](expo-secure-store+14.2.4+004+fail-on-update.patch)
+
+- Reason:
 
-    This is fixed with this patch, but instead of hardcoding solution for above needs, the patch adds it into the SecureStore options
     ```
+    If a value already exists in the SecureStore and a new one is saved, the existing value is simply overwritten.
+    To avoid unexpected behaviour, set the 'failOnUpdate' flag to true to trigger an error when the given key is already in the store.
+    ```
+
+- Upstream PR/issue: TBA
+- E/App issue: https://github.com/Expensify/App/issues/75225
+- PR introducing patch: https://github.com/Expensify/App/pull/76288
+
+### [expo-secure-store+14.2.4+005+force-read-authentication-on-simulators.patch](expo-secure-store+14.2.4+005+force-read-authentication-on-simulators.patch)
+
+- Reason:
+
+    ```
+    The LocalAuthentication behaves slightly differently on iOS simulators.
+    In numerous cases, the authentication prompts are skipped on simulators (as opposed to real devices).
+    Setting 'forceReadAuthenticationOnSimulators' flag to true forces the prompt to appear on simulators when a value with the `requireAuthentication` flag set to true is read.
+    The flag is added is purely for testing the app on simulators, in cases where the prompt does not appear when the value is read.
+    It has no effect on real devices.
+    ```
+
+- Upstream PR/issue: TBA
+- E/App issue: https://github.com/Expensify/App/issues/75225
+- PR introducing patch: https://github.com/Expensify/App/pull/76288
 
-- Upstream PR/issue: 🛑
-- E/App issue: No issue, this patch adjust the library for our needs.
-- PR introducing patch: https://github.com/Expensify/App/pull/69863
\ No newline at end of file
diff --git a/patches/expo-secure-store/expo-secure-store+14.2.4+001+additional-config-options.patch b/patches/expo-secure-store/expo-secure-store+14.2.4+001+additional-config-options.patch
deleted file mode 100644
index d766eb5c0716..000000000000
--- a/patches/expo-secure-store/expo-secure-store+14.2.4+001+additional-config-options.patch
+++ /dev/null
@@ -1,1179 +0,0 @@
-diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
-index a281b88..dc84eb4 100644
---- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
-+++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
-@@ -2,6 +2,7 @@ package expo.modules.securestore
-
- import android.annotation.SuppressLint
- import android.app.Activity
-+import android.app.KeyguardManager
- import android.content.Context
- import android.os.Build
- import androidx.biometric.BiometricManager
-@@ -19,17 +20,18 @@ class AuthenticationHelper(
- ) {
-   private var isAuthenticating = false
-
--  suspend fun authenticateCipher(cipher: Cipher, requiresAuthentication: Boolean, title: String): Cipher {
--    if (requiresAuthentication) {
--      return openAuthenticationPrompt(cipher, title).cryptoObject?.cipher
--        ?: throw AuthenticationException("Couldn't get cipher from authentication result")
-+  suspend fun authenticateCipher(cipher: Cipher, title: String, enableCredentialsAlternative: Boolean): BiometricPrompt.AuthenticationResult {
-+    val promptResult = openAuthenticationPrompt(cipher, title, enableCredentialsAlternative)
-+    if (promptResult.cryptoObject?.cipher == null) {
-+      throw AuthenticationException("Couldn't get cipher from authentication result")
-     }
--    return cipher
-+    return promptResult
-   }
-
-   private suspend fun openAuthenticationPrompt(
-     cipher: Cipher,
--    title: String
-+    title: String,
-+    enableCredentialsAlternative: Boolean
-   ): BiometricPrompt.AuthenticationResult {
-     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
-       throw AuthenticationException("Biometric authentication requires Android API 23")
-@@ -41,11 +43,16 @@ class AuthenticationHelper(
-     isAuthenticating = true
-
-     try {
--      assertBiometricsSupport()
-+      if (enableCredentialsAlternative) {
-+        assertDeviceSecurity()
-+      } else {
-+        assertBiometricsSupport()
-+      }
-+
-       val fragmentActivity = getCurrentActivity() as? FragmentActivity
-         ?: throw AuthenticationException("Cannot display biometric prompt when the app is not in the foreground")
-
--      val authenticationPrompt = AuthenticationPrompt(fragmentActivity, context, title)
-+      val authenticationPrompt = AuthenticationPrompt(fragmentActivity, context, title, enableCredentialsAlternative)
-
-       return withContext(Dispatchers.Main.immediate) {
-         return@withContext authenticationPrompt.authenticate(cipher)
-@@ -56,6 +63,14 @@ class AuthenticationHelper(
-     }
-   }
-
-+  fun assertDeviceSecurity() {
-+    val manager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
-+    val isSecure = manager.isDeviceSecure
-+    if (!isSecure) {
-+      throw AuthenticationException("No authentication method available")
-+    }
-+  }
-+
-   fun assertBiometricsSupport() {
-     val biometricManager = BiometricManager.from(context)
-     @SuppressLint("SwitchIntDef") // BiometricManager.BIOMETRIC_SUCCESS shouldn't do anything
-diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt
-index e5729cc..0fd4242 100644
---- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt
-+++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt
-@@ -1,5 +1,7 @@
- package expo.modules.securestore
-
-+import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
-+import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
- import android.content.Context
- import androidx.biometric.BiometricPrompt
- import androidx.biometric.BiometricPrompt.PromptInfo
-@@ -11,11 +13,12 @@ import kotlin.coroutines.resume
- import kotlin.coroutines.resumeWithException
- import kotlin.coroutines.suspendCoroutine
-
--class AuthenticationPrompt(private val currentActivity: FragmentActivity, context: Context, title: String) {
-+class AuthenticationPrompt(private val currentActivity: FragmentActivity, context: Context, title: String, enableCredentialsAlternative: Boolean) {
-+  private var authType: Int = if (enableCredentialsAlternative) BIOMETRIC_STRONG or DEVICE_CREDENTIAL else BIOMETRIC_STRONG
-   private var executor: Executor = ContextCompat.getMainExecutor(context)
-   private var promptInfo = PromptInfo.Builder()
-     .setTitle(title)
--    .setNegativeButtonText(context.getString(android.R.string.cancel))
-+    .setAllowedAuthenticators(authType)
-     .build()
-
-   suspend fun authenticate(cipher: Cipher): BiometricPrompt.AuthenticationResult? =
-diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
-index 0fef884..822caa5 100644
---- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
-+++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
-@@ -36,23 +36,23 @@ open class SecureStoreModule : Module() {
-
-     AsyncFunction("setValueWithKeyAsync") Coroutine { value: String?, key: String?, options: SecureStoreOptions ->
-       key ?: throw NullKeyException()
--      return@Coroutine setItemImpl(key, value, options, false)
-+      return@Coroutine setItemImpl(key, value, options, false).ordinal
-     }
-
-     AsyncFunction("getValueWithKeyAsync") Coroutine { key: String, options: SecureStoreOptions ->
--      return@Coroutine getItemImpl(key, options)
-+      return@Coroutine getItemImpl(key, options).values
-     }
-
-     Function("setValueWithKeySync") { value: String?, key: String?, options: SecureStoreOptions ->
-       key ?: throw NullKeyException()
-       return@Function runBlocking {
--        return@runBlocking setItemImpl(key, value, options, keyIsInvalidated = false)
-+        return@runBlocking setItemImpl(key, value, options, keyIsInvalidated = false).ordinal
-       }
-     }
-
-     Function("getValueWithKeySync") { key: String, options: SecureStoreOptions ->
-       return@Function runBlocking {
--        return@runBlocking getItemImpl(key, options)
-+        return@runBlocking getItemImpl(key, options).values
-       }
-     }
-
-@@ -85,7 +85,7 @@ open class SecureStoreModule : Module() {
-     }
-   }
-
--  private suspend fun getItemImpl(key: String, options: SecureStoreOptions): String? {
-+  private suspend fun getItemImpl(key: String, options: SecureStoreOptions): SecureStoreFeedback<out String?> {
-     // We use a SecureStore-specific shared preferences file, which lets us do things like enumerate
-     // its entries or clear all of them
-     val prefs: SharedPreferences = getSharedPreferences()
-@@ -95,10 +95,10 @@ open class SecureStoreModule : Module() {
-     } else if (prefs.contains(key)) { // For backwards-compatibility try to read using the old key format
-       return readJSONEncodedItem(key, prefs, options)
-     }
--    return null
-+    return SecureStoreFeedback(null)
-   }
-
--  private suspend fun readJSONEncodedItem(key: String, prefs: SharedPreferences, options: SecureStoreOptions): String? {
-+  private suspend fun readJSONEncodedItem(key: String, prefs: SharedPreferences, options: SecureStoreOptions): SecureStoreFeedback<out String?> {
-     val keychainAwareKey = createKeychainAwareKey(key, options.keychainService)
-
-     val legacyEncryptedItemString = prefs.getString(key, null)
-@@ -117,7 +117,7 @@ open class SecureStoreModule : Module() {
-       ""
-     }
-
--    encryptedItemString ?: return null
-+    encryptedItemString ?: return SecureStoreFeedback(null)
-
-     val encryptedItem: JSONObject = try {
-       JSONObject(encryptedItemString)
-@@ -140,22 +140,26 @@ open class SecureStoreModule : Module() {
-                 "This situation occurs when the app is reinstalled. The value will be removed to avoid future errors. Returning null"
-             )
-             deleteItemImpl(key, options)
--            return null
-+            return SecureStoreFeedback(null)
-           }
-+
-           return mAESEncryptor.decryptItem(key, encryptedItem, secretKeyEntry, options, authenticationHelper)
-         }
-+
-         HybridAESEncryptor.NAME -> {
-           val privateKeyEntry = getKeyEntryCompat(PrivateKeyEntry::class.java, hybridAESEncryptor, options, requireAuthentication, usesKeystoreSuffix)
--            ?: return null
-+            ?: return SecureStoreFeedback(null)
-+
-           return hybridAESEncryptor.decryptItem(key, encryptedItem, privateKeyEntry, options, authenticationHelper)
-         }
-+
-         else -> {
-           throw DecryptException("The item for key $key in SecureStore has an unknown encoding scheme $scheme)", key, options.keychainService)
-         }
-       }
-     } catch (e: KeyPermanentlyInvalidatedException) {
-       Log.w(TAG, "The requested key has been permanently invalidated. Returning null")
--      return null
-+      return SecureStoreFeedback(null)
-     } catch (e: BadPaddingException) {
-       // The key from the KeyStore is unable to decode the entry. This is because a new key was generated, but the entries are encrypted using the old one.
-       // This usually means that the user has reinstalled the app. We can safely remove the old value and return null as it's impossible to decrypt it.
-@@ -165,7 +169,7 @@ open class SecureStoreModule : Module() {
-           "The entry in shared preferences is out of sync with the keystore. It will be removed, returning null."
-       )
-       deleteItemImpl(key, options)
--      return null
-+      return SecureStoreFeedback(null)
-     } catch (e: GeneralSecurityException) {
-       throw (DecryptException(e.message, key, options.keychainService, e))
-     } catch (e: CodedException) {
-@@ -175,7 +179,7 @@ open class SecureStoreModule : Module() {
-     }
-   }
-
--  private suspend fun setItemImpl(key: String, value: String?, options: SecureStoreOptions, keyIsInvalidated: Boolean) {
-+  private suspend fun setItemImpl(key: String, value: String?, options: SecureStoreOptions, keyIsInvalidated: Boolean): SecureStoreAuthType {
-     val keychainAwareKey = createKeychainAwareKey(key, options.keychainService)
-     val prefs: SharedPreferences = getSharedPreferences()
-
-@@ -184,7 +188,11 @@ open class SecureStoreModule : Module() {
-       if (!success) {
-         throw WriteException("Could not write a null value to SecureStore", key, options.keychainService)
-       }
--      return
-+      return SecureStoreAuthType.NONE
-+    }
-+
-+    if (prefs.contains(keychainAwareKey) && options.failOnDuplicate) {
-+      throw WriteException("Key already exists", key, options.keychainService)
-     }
-
-     try {
-@@ -200,8 +208,11 @@ open class SecureStoreModule : Module() {
-        use in the encrypted JSON item so that we know how to decode and decrypt it when reading
-        back a value.
-        */
--      val secretKeyEntry: SecretKeyEntry = getOrCreateKeyEntry(SecretKeyEntry::class.java, mAESEncryptor, options, options.requireAuthentication)
--      val encryptedItem = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper)
-+      val secretKeyEntry: SecretKeyEntry = getOrCreateKeyEntry(
-+        SecretKeyEntry::class.java, mAESEncryptor, options, options.requireAuthentication
-+      )
-+      val encryptResult = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper, options.enableCredentialsAlternative)
-+      val encryptedItem = encryptResult.value
-       encryptedItem.put(SCHEME_PROPERTY, AESEncryptor.NAME)
-       saveEncryptedItem(encryptedItem, prefs, keychainAwareKey, options.requireAuthentication, options.keychainService)
-
-@@ -209,6 +220,9 @@ open class SecureStoreModule : Module() {
-       if (prefs.contains(key)) {
-         prefs.edit().remove(key).apply()
-       }
-+
-+      return encryptResult.authType
-+
-     } catch (e: KeyPermanentlyInvalidatedException) {
-       if (!keyIsInvalidated) {
-         Log.w(TAG, "Key has been invalidated, retrying with the key deleted")
-@@ -343,7 +357,11 @@ open class SecureStoreModule : Module() {
-     return getKeyEntry(keyStoreEntryClass, encryptor, options, requireAuthentication) ?: run {
-       // Android won't allow us to generate the keys if the device doesn't support biometrics or no biometrics are enrolled
-       if (requireAuthentication) {
--        authenticationHelper.assertBiometricsSupport()
-+        if (options.enableCredentialsAlternative) {
-+          authenticationHelper.assertDeviceSecurity()
-+        } else {
-+          authenticationHelper.assertBiometricsSupport()
-+        }
-       }
-       encryptor.initializeKeyStoreEntry(keyStore, options)
-     }
-diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
-index 79a600f..ad78439 100644
---- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
-+++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
-@@ -1,5 +1,6 @@
- package expo.modules.securestore
-
-+import androidx.biometric.BiometricPrompt
- import expo.modules.kotlin.records.Field
- import expo.modules.kotlin.records.Record
- import java.io.Serializable
-@@ -8,5 +9,39 @@ class SecureStoreOptions(
-   // Prompt can't be an empty string
-   @Field var authenticationPrompt: String = " ",
-   @Field var keychainService: String = SecureStoreModule.DEFAULT_KEYSTORE_ALIAS,
--  @Field var requireAuthentication: Boolean = false
-+  @Field var requireAuthentication: Boolean = false,
-+  @Field var failOnDuplicate: Boolean = false,
-+  @Field var enableCredentialsAlternative: Boolean = false
- ) : Record, Serializable
-+
-+enum class SecureStoreAuthType(index: Int) {
-+  UNKNOWN(BiometricPrompt.AUTHENTICATION_RESULT_TYPE_UNKNOWN),
-+  CREDENTIAL(BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL),
-+  BIOMETRIC(BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC),
-+
-+  /** Prompt failed, no authentication was used at all */
-+  NONE(0)
-+}
-+
-+data class SecureStoreFeedback<T>(
-+  val value: T,
-+  val authenticationResult: BiometricPrompt.AuthenticationResult? = null
-+) {
-+  @Field var authType: SecureStoreAuthType = when (authenticationResult?.authenticationType) {
-+    BiometricPrompt.AUTHENTICATION_RESULT_TYPE_UNKNOWN -> {
-+      SecureStoreAuthType.UNKNOWN
-+    }
-+    BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL -> {
-+      SecureStoreAuthType.CREDENTIAL
-+    }
-+    BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC -> {
-+      SecureStoreAuthType.BIOMETRIC
-+    }
-+    else -> {
-+      SecureStoreAuthType.NONE
-+    }
-+  }
-+
-+  /** Used to return easily convertible values to JS code */
-+  @Field var values: Pair<T, Int> = Pair(value, authType.ordinal)
-+}
-\ No newline at end of file
-diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
-index 3a12dc9..d5b797b 100644
---- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
-+++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
-@@ -1,11 +1,14 @@
- package expo.modules.securestore.encryptors
-
--import android.annotation.TargetApi
-+import android.os.Build
- import android.security.keystore.KeyGenParameterSpec
- import android.security.keystore.KeyProperties
- import android.util.Base64
-+import androidx.annotation.RequiresApi
-+import androidx.biometric.BiometricPrompt
- import expo.modules.securestore.AuthenticationHelper
- import expo.modules.securestore.DecryptException
-+import expo.modules.securestore.SecureStoreFeedback
- import expo.modules.securestore.SecureStoreModule
- import expo.modules.securestore.SecureStoreOptions
- import org.json.JSONException
-@@ -50,17 +53,24 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
-     return "${getKeyStoreAlias(options)}:$suffix"
-   }
-
--  @TargetApi(23)
-+  @RequiresApi(Build.VERSION_CODES.R)
-   @Throws(GeneralSecurityException::class)
--  override fun initializeKeyStoreEntry(keyStore: KeyStore, options: SecureStoreOptions): KeyStore.SecretKeyEntry {
-+  override fun initializeKeyStoreEntry(
-+    keyStore: KeyStore, options: SecureStoreOptions
-+  ): KeyStore.SecretKeyEntry {
-     val extendedKeystoreAlias = getExtendedKeyStoreAlias(options, options.requireAuthentication)
-     val keyPurposes = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
-
-+    val authType =
-+      if (options.enableCredentialsAlternative) KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL
-+      else KeyProperties.AUTH_BIOMETRIC_STRONG
-+
-     val algorithmSpec: AlgorithmParameterSpec = KeyGenParameterSpec.Builder(extendedKeystoreAlias, keyPurposes)
-       .setKeySize(AES_KEY_SIZE_BITS)
-       .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
-       .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
-       .setUserAuthenticationRequired(options.requireAuthentication)
-+      .setUserAuthenticationParameters(0, authType)
-       .build()
-
-     val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, keyStore.provider)
-@@ -78,16 +88,25 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
-     keyStoreEntry: KeyStore.SecretKeyEntry,
-     requireAuthentication: Boolean,
-     authenticationPrompt: String,
--    authenticationHelper: AuthenticationHelper
--  ): JSONObject {
-+    authenticationHelper: AuthenticationHelper,
-+    enableCredentialsAlternative: Boolean,
-+  ): SecureStoreFeedback<JSONObject> {
-     val secretKey = keyStoreEntry.secretKey
-     val cipher = Cipher.getInstance(AES_CIPHER)
-     cipher.init(Cipher.ENCRYPT_MODE, secretKey)
-
-     val gcmSpec = cipher.parameters.getParameterSpec(GCMParameterSpec::class.java)
--    val authenticatedCipher = authenticationHelper.authenticateCipher(cipher, requireAuthentication, authenticationPrompt)
-+    var promptResult: BiometricPrompt.AuthenticationResult? = null
-+    val authenticatedCipher: Cipher
-+
-+    if (requireAuthentication) {
-+      promptResult = authenticationHelper.authenticateCipher(cipher, authenticationPrompt, enableCredentialsAlternative)
-+      authenticatedCipher = promptResult.cryptoObject?.cipher ?: cipher
-+    } else {
-+      authenticatedCipher = cipher
-+    }
-
--    return createEncryptedItemWithCipher(plaintextValue, authenticatedCipher, gcmSpec)
-+    return SecureStoreFeedback(createEncryptedItemWithCipher(plaintextValue, authenticatedCipher, gcmSpec), promptResult)
-   }
-
-   internal fun createEncryptedItemWithCipher(
-@@ -114,7 +133,7 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
-     keyStoreEntry: KeyStore.SecretKeyEntry,
-     options: SecureStoreOptions,
-     authenticationHelper: AuthenticationHelper
--  ): String {
-+  ): SecureStoreFeedback<String> {
-     val ciphertext = encryptedItem.getString(CIPHERTEXT_PROPERTY)
-     val ivString = encryptedItem.getString(IV_PROPERTY)
-     val authenticationTagLength = encryptedItem.getInt(GCM_AUTHENTICATION_TAG_LENGTH_PROPERTY)
-@@ -128,8 +147,18 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
-       throw DecryptException("Authentication tag length must be at least $MIN_GCM_AUTHENTICATION_TAG_LENGTH bits long", key, options.keychainService)
-     }
-     cipher.init(Cipher.DECRYPT_MODE, keyStoreEntry.secretKey, gcmSpec)
--    val unlockedCipher = authenticationHelper.authenticateCipher(cipher, requiresAuthentication, options.authenticationPrompt)
--    return String(unlockedCipher.doFinal(ciphertextBytes), StandardCharsets.UTF_8)
-+
-+    var promptResult: BiometricPrompt.AuthenticationResult? = null
-+    val unlockedCipher: Cipher
-+
-+    if (requiresAuthentication) {
-+      promptResult = authenticationHelper.authenticateCipher(cipher, options.authenticationPrompt, options.enableCredentialsAlternative)
-+      unlockedCipher = promptResult.cryptoObject?.cipher ?: cipher
-+    } else {
-+      unlockedCipher = cipher
-+    }
-+
-+    return SecureStoreFeedback(String(unlockedCipher.doFinal(ciphertextBytes), StandardCharsets.UTF_8), promptResult)
-   }
-
-   companion object {
-diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
-index fb42599..0055b62 100644
---- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
-+++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
-@@ -7,6 +7,7 @@ import android.util.Base64
- import expo.modules.securestore.AuthenticationHelper
- import expo.modules.securestore.EncryptException
- import expo.modules.securestore.KeyStoreException
-+import expo.modules.securestore.SecureStoreFeedback
- import expo.modules.securestore.SecureStoreModule
- import expo.modules.securestore.SecureStoreOptions
- import org.json.JSONException
-@@ -69,8 +70,9 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
-     keyStoreEntry: KeyStore.PrivateKeyEntry,
-     requireAuthentication: Boolean,
-     authenticationPrompt: String,
--    authenticationHelper: AuthenticationHelper
--  ): JSONObject {
-+    authenticationHelper: AuthenticationHelper,
-+    enableCredentialsAlternative: Boolean,
-+  ): SecureStoreFeedback<JSONObject> {
-     // This should never be called after we dropped Android SDK 22 support.
-     throw EncryptException(
-       "HybridAESEncryption should not be used on Android SDK >= 23. This shouldn't happen. " +
-@@ -86,8 +88,8 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
-     encryptedItem: JSONObject,
-     keyStoreEntry: KeyStore.PrivateKeyEntry,
-     options: SecureStoreOptions,
--    authenticationHelper: AuthenticationHelper
--  ): String {
-+    authenticationHelper: AuthenticationHelper,
-+  ): SecureStoreFeedback<String> {
-     // Decrypt the encrypted symmetric key
-     val encryptedSecretKeyString = encryptedItem.getString(ENCRYPTED_SECRET_KEY_PROPERTY)
-     val encryptedSecretKeyBytes = Base64.decode(encryptedSecretKeyString, Base64.DEFAULT)
-diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
-index e493467..c880ee0 100644
---- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
-+++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
-@@ -1,6 +1,7 @@
- package expo.modules.securestore.encryptors
-
- import expo.modules.securestore.AuthenticationHelper
-+import expo.modules.securestore.SecureStoreFeedback
- import expo.modules.securestore.SecureStoreOptions
- import org.json.JSONException
- import org.json.JSONObject
-@@ -25,8 +26,9 @@ interface KeyBasedEncryptor<E : KeyStore.Entry> {
-     keyStoreEntry: E,
-     requireAuthentication: Boolean,
-     authenticationPrompt: String,
--    authenticationHelper: AuthenticationHelper
--  ): JSONObject
-+    authenticationHelper: AuthenticationHelper,
-+    enableCredentialsAlternative: Boolean,
-+  ): SecureStoreFeedback<JSONObject>
-
-   @Throws(GeneralSecurityException::class, JSONException::class)
-   suspend fun decryptItem(
-@@ -34,6 +36,6 @@ interface KeyBasedEncryptor<E : KeyStore.Entry> {
-     encryptedItem: JSONObject,
-     keyStoreEntry: E,
-     options: SecureStoreOptions,
--    authenticationHelper: AuthenticationHelper
--  ): String
-+    authenticationHelper: AuthenticationHelper,
-+  ): SecureStoreFeedback<String>
- }
-diff --git a/node_modules/expo-secure-store/build/SecureStore.d.ts b/node_modules/expo-secure-store/build/SecureStore.d.ts
-index d5cd157..ca1cd76 100644
---- a/node_modules/expo-secure-store/build/SecureStore.d.ts
-+++ b/node_modules/expo-secure-store/build/SecureStore.d.ts
-@@ -1,4 +1,53 @@
- export type KeychainAccessibilityConstant = number;
-+
-+/**
-+ * Authentication type returned by the SecureStore after reading item or saving it to the store.
-+ */
-+export declare const AUTH_TYPE = {
-+    /**
-+     * This is purely for backwards compatibility.
-+     * Although it is not listed as a return value of the getAuthenticationType() method,
-+     * it is still present in the Android code.
-+     * @see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.AuthenticationResult#getAuthenticationType()
-+     * @see https://developer.android.com/reference/androidx/biometric/BiometricPrompt#AUTHENTICATION_RESULT_TYPE_UNKNOWN()
-+     * @platform android
-+     */
-+    UNKNOWN: -1,
-+    /**
-+     * Returned when the authentication fails
-+     * @platform android
-+     * @platform ios
-+     */
-+    NONE: 0,
-+    /**
-+     * Generic type, not specified whether it was a passcode or pattern.
-+     * @platform android
-+     * @platform ios
-+     */
-+    CREDENTIALS: 1,
-+    /**
-+     * Generic type, not specified whether it was a face scan or a fingerprint
-+     * @platform android
-+     */
-+    BIOMETRICS: 2,
-+    /**
-+     * FaceID was used to authenticate
-+     * @platform ios
-+     */
-+    FACE_ID: 3,
-+    /**
-+     * TouchID was used to authenticate
-+     * @platform ios
-+     */
-+    TOUCH_ID: 4,
-+    /**
-+     * OpticID was used to authenticate (reserved by apple, used on Apple Vision Pro, not iOS)
-+     */
-+    OPTIC_ID: 5
-+} as const;
-+
-+type AuthType = (typeof AUTH_TYPE)[keyof typeof AUTH_TYPE];
-+
- /**
-  * The data in the keychain item cannot be accessed after a restart until the device has been
-  * unlocked once by the user. This may be useful if you need to access the item when the phone
-@@ -78,6 +127,45 @@ export type SecureStoreOptions = {
-      * @platform ios
-      */
-     accessGroup?: string;
-+
-+    /**
-+     * If value already exists, throw an error
-+     *
-+     * On Android it throws an error before the prompt for authentication.
-+     * On iOS it throws an error after authentication.
-+     */
-+    failOnDuplicate?: boolean;
-+    /**
-+     * On iOS, the system sometimes skips authentication if it has been done recently.
-+     * This also applies to actions such as unlocking the screen.
-+     * Setting this flag to true will ensure that authentication is always required before accessing the store.
-+     *
-+     * Use this wisely, when the user did not authenticated recently, setting this to true will lead to prompt
-+     * being displayed twice when accessing the store value.
-+     * The best use-case for this flag is when testing the behavior on iOS simulator,
-+     * since the auth there may not be prompted at all without this flag.
-+     *
-+     * @warning This flag works only for async version of the save/read SecureStore methods.
-+     * @platform ios
-+     */
-+    alwaysAskForAuth?: boolean;
-+    /**
-+     * On iOS, the system does not ask for auth when saving the value to the SecureStore.
-+     * The Android however, displays the authentication prompt when saving the value.
-+     * To keep the behavior on every platform similar as much as possible,
-+     * setting this flag to true will ensure that authentication is required when saving a value to the store.
-+     *
-+     * @warning This flag works only for async version of the save/read SecureStore methods.
-+     * @platform ios
-+     */
-+    askForAuthOnSave?: boolean;
-+    /**
-+     * On Android, there is often no fallback option if biometrics fail.
-+     * Therefore, we may want to allow the user to authenticate using credentials if they prefer to.
-+     *
-+     * @platform android
-+     */
-+    enableCredentialsAlternative?: boolean;
- };
- /**
-  * Returns whether the SecureStore API is enabled on the current device. This does not check the app
-@@ -109,7 +197,7 @@ export declare function deleteItemAsync(key: string, options?: SecureStoreOption
-  * > After a key has been invalidated, it becomes impossible to read its value.
-  * > This only applies to values stored with `requireAuthentication` set to `true`.
-  */
--export declare function getItemAsync(key: string, options?: SecureStoreOptions): Promise<string | null>;
-+export declare function getItemAsync(key: string, options?: SecureStoreOptions): Promise<[string | null, AuthType]>;
- /**
-  * Stores a key–value pair.
-  *
-@@ -119,7 +207,7 @@ export declare function getItemAsync(key: string, options?: SecureStoreOptions):
-  *
-  * @return A promise that rejects if value cannot be stored on the device.
-  */
--export declare function setItemAsync(key: string, value: string, options?: SecureStoreOptions): Promise<void>;
-+export declare function setItemAsync(key: string, value: string, options?: SecureStoreOptions): Promise<AuthType>;
- /**
-  * Stores a key–value pair synchronously.
-  * > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when the `requireAuthentication` option is set to `true` until the user authenticates.
-@@ -129,7 +217,7 @@ export declare function setItemAsync(key: string, value: string, options?: Secur
-  * @param options An [`SecureStoreOptions`](#securestoreoptions) object.
-  *
-  */
--export declare function setItem(key: string, value: string, options?: SecureStoreOptions): void;
-+export declare function setItem(key: string, value: string, options?: SecureStoreOptions): AuthType;
- /**
-  * Synchronously reads the stored value associated with the provided key.
-  * > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when reading a value with `requireAuthentication`
-@@ -140,7 +228,7 @@ export declare function setItem(key: string, value: string, options?: SecureStor
-  * @return Previously stored value. It resolves with `null` if there is no entry
-  * for the given key or if the key has been invalidated.
-  */
--export declare function getItem(key: string, options?: SecureStoreOptions): string | null;
-+export declare function getItem(key: string, options?: SecureStoreOptions): [string | null, AuthType];
- /**
-  * Checks if the value can be saved with `requireAuthentication` option enabled.
-  * @return `true` if the device supports biometric authentication and the enrolled method is sufficiently secure. Otherwise, returns `false`. Always returns false on tvOS.
-diff --git a/node_modules/expo-secure-store/build/SecureStore.js b/node_modules/expo-secure-store/build/SecureStore.js
-index 4d87b38..4399e5e 100644
---- a/node_modules/expo-secure-store/build/SecureStore.js
-+++ b/node_modules/expo-secure-store/build/SecureStore.js
-@@ -1,6 +1,52 @@
- import ExpoSecureStore from './ExpoSecureStore';
- import { byteCountOverLimit, VALUE_BYTES_LIMIT } from './byteCounter';
- // @needsAudit
-+
-+/**
-+ * Authentication type returned by the SecureStore after reading item or saving it to the store.
-+ */
-+export const AUTH_TYPE = {
-+    /**
-+     * This is purely for backwards compatibility.
-+     * Although it is not listed as a return value of the getAuthenticationType() method,
-+     * it is still present in the Android code.
-+     * @see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.AuthenticationResult#getAuthenticationType()
-+     * @see https://developer.android.com/reference/androidx/biometric/BiometricPrompt#AUTHENTICATION_RESULT_TYPE_UNKNOWN()
-+     * @platform android
-+     */
-+    UNKNOWN: -1,
-+    /**
-+     * Returned when the authentication fails
-+     * @platform android
-+     * @platform ios
-+     */
-+    NONE: 0,
-+    /**
-+     * Generic type, not specified whether it was a passcode or pattern.
-+     * @platform android
-+     * @platform ios
-+     */
-+    CREDENTIALS: 1,
-+    /**
-+     * Generic type, not specified whether it was a face scan or a fingerprint
-+     * @platform android
-+     */
-+    BIOMETRICS: 2,
-+    /**
-+     * FaceID was used to authenticate
-+     * @platform ios
-+     */
-+    FACE_ID: 3,
-+    /**
-+     * TouchID was used to authenticate
-+     * @platform ios
-+     */
-+    TOUCH_ID: 4,
-+    /**
-+     * OpticID was used to authenticate (reserved by apple, used on Apple Vision Pro, not iOS)
-+     */
-+    OPTIC_ID: 5
-+};
- /**
-  * The data in the keychain item cannot be accessed after a restart until the device has been
-  * unlocked once by the user. This may be useful if you need to access the item when the phone
-@@ -102,7 +148,7 @@ export async function setItemAsync(key, value, options = {}) {
-     if (!isValidValue(value)) {
-         throw new Error(`Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.`);
-     }
--    await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
-+    return await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
- }
- /**
-  * Stores a key–value pair synchronously.
-diff --git a/node_modules/expo-secure-store/ios/SecureStoreModule.swift b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
-index 439b08d..98dab3e 100644
---- a/node_modules/expo-secure-store/ios/SecureStoreModule.swift
-+++ b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
-@@ -4,6 +4,42 @@ import LocalAuthentication
- #endif
- import Security
-
-+@available(iOS 11.2, *)
-+public enum AuthType: Int, @unchecked Sendable {
-+  /// The device does not support biometry.
-+  case none = 0
-+
-+  /// The device supports device credentials
-+  case credentials = 1
-+
-+  /// Generic type, not specified whether it was a faceID or touchID
-+  case biometrics = 2
-+
-+  /// The device supports Face ID.
-+  case faceID = 3
-+
-+  /// The device supports Touch ID.
-+  case touchID = 4
-+
-+  /// The device supports Optic ID
-+  case opticID = 5
-+}
-+
-+struct SecureStoreFeedback<T> {
-+  var value: T
-+  var authType: Int = AuthType.none.rawValue
-+  var values: Array<Any> {
-+    get { return [value, authType] }
-+  }
-+}
-+
-+struct SecureStoreRuntimeError: LocalizedError {
-+  let errorDescription: String?
-+  init(_ description: String) {
-+    self.errorDescription = description
-+  }
-+}
-+
- public final class SecureStoreModule: Module {
-   public func definition() -> ModuleDefinition {
-     Name("ExpoSecureStore")
-@@ -18,57 +54,137 @@ public final class SecureStoreModule: Module {
-       "WHEN_UNLOCKED_THIS_DEVICE_ONLY": SecureStoreAccessible.whenUnlockedThisDeviceOnly.rawValue
-     ])
-
--    AsyncFunction("getValueWithKeyAsync") { (key: String, options: SecureStoreOptions) -> String? in
--      return try get(with: key, options: options)
-+    AsyncFunction("getValueWithKeyAsync") { (key: String, options: SecureStoreOptions) in
-+      if options.requireAuthentication && options.alwaysAskForAuth {
-+        try await askForAuth(
-+          prompt: options.authenticationPrompt
-+          ?? "Authentication required"
-+        )
-+      }
-+      return getSecureStoreFeedback(value: try get(with: key, options: options)).values
-     }
-
--    Function("getValueWithKeySync") { (key: String, options: SecureStoreOptions) -> String? in
--      return try get(with: key, options: options)
-+    Function("getValueWithKeySync") { (key: String, options: SecureStoreOptions) in
-+      return getSecureStoreFeedback(value: try get(with: key, options: options)).values
-     }
-
--    AsyncFunction("setValueWithKeyAsync") { (value: String, key: String, options: SecureStoreOptions) -> Bool in
-+    AsyncFunction("setValueWithKeyAsync") { (value: String, key: String, options: SecureStoreOptions) -> Int in
-       guard let key = validate(for: key) else {
-         throw InvalidKeyException()
-       }
-
--      return try set(value: value, with: key, options: options)
-+      if options.requireAuthentication && (
-+        options.alwaysAskForAuth || options.askForAuthOnSave
-+      ) {
-+        try await askForAuth(
-+          prompt: options.authenticationPrompt
-+          ?? "Authentication required"
-+        )
-+      }
-+
-+      let result = try set(value: value, with: key, options: options)
-+
-+      if !result {
-+        return AuthType.none.rawValue
-+      }
-+
-+      return getSecureStoreFeedback(value: true).authType
-     }
-
--    Function("setValueWithKeySync") {(value: String, key: String, options: SecureStoreOptions) -> Bool in
-+    Function("setValueWithKeySync") {(value: String, key: String, options: SecureStoreOptions) -> Int in
-       guard let key = validate(for: key) else {
-         throw InvalidKeyException()
-       }
-
--      return try set(value: value, with: key, options: options)
-+      let result = try set(value: value, with: key, options: options)
-+
-+      if !result {
-+        return AuthType.none.rawValue
-+      }
-+
-+      return getSecureStoreFeedback(value: true).authType
-     }
-
-     AsyncFunction("deleteValueWithKeyAsync") { (key: String, options: SecureStoreOptions) in
--      let noAuthSearchDictionary = query(with: key, options: options, requireAuthentication: false)
--      let authSearchDictionary = query(with: key, options: options, requireAuthentication: true)
--      let legacySearchDictionary = query(with: key, options: options)
-+      let noAuthSearchDictionary = try query(with: key, options: options, requireAuthentication: false)
-+      let authSearchDictionary = try query(with: key, options: options, requireAuthentication: true)
-+      let legacySearchDictionary = try query(with: key, options: options)
-
-       SecItemDelete(legacySearchDictionary as CFDictionary)
-       SecItemDelete(authSearchDictionary as CFDictionary)
-       SecItemDelete(noAuthSearchDictionary as CFDictionary)
-     }
-
--    Function("canUseBiometricAuthentication") {() -> Bool in
--      #if os(tvOS)
--      return false
--      #else
--      let context = LAContext()
--      var error: NSError?
--      let isBiometricsSupported: Bool = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
-+    Function("canUseBiometricAuthentication") { () -> Bool in
-+      return areBiometricsEnabled()
-+    }
-+  }
-
--      if error != nil {
--        return false
--      }
--      return isBiometricsSupported
--      #endif
-+  private func askForAuth(prompt: String) async throws {
-+    let context = LAContext()
-+    context.localizedReason = prompt
-+
-+    guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) else {
-+      throw SecureStoreRuntimeError("No authentication method available")
-     }
-+
-+    let result = try await context.evaluatePolicy(
-+      LAPolicy.deviceOwnerAuthentication,
-+      localizedReason: context.localizedReason
-+    )
-+
-+    if !result {
-+      throw SecureStoreRuntimeError("Unable to authenticate")
-+    }
-+  }
-+
-+  private func getAuthType() -> AuthType {
-+    if !areBiometricsEnabled() {return AuthType.credentials}
-+    let biometryType = LAContext().biometryType
-+
-+    switch biometryType {
-+    case .faceID: return .faceID
-+    case .touchID: return .touchID
-+    case .opticID: return .opticID // available since iOS 17
-+    case .none: fallthrough // this one continues to the next line
-+    @unknown default: return .credentials
-+    }
-+  }
-+
-+  private func getSecureStoreFeedback<T>(value: T) -> SecureStoreFeedback<T> {
-+    return SecureStoreFeedback(value: value, authType: getAuthType().rawValue)
-   }
-
--  private func get(with key: String, options: SecureStoreOptions) throws -> String? {
-+  private func getAccessControl(accessibility: CFString) throws -> SecAccessControl
-+  {
-+    guard
-+      Bundle.main.infoDictionary?["NSFaceIDUsageDescription"] as? String
-+        != nil
-+    else {
-+      throw MissingPlistKeyException()
-+    }
-+
-+    var error: Unmanaged<CFError>? = nil
-+    guard
-+      let accessOptions = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility, .userPresence, &error)
-+    else {
-+      let errorCode = error.map { CFErrorGetCode($0.takeRetainedValue()) }
-+      throw SecAccessControlError(errorCode)
-+    }
-+
-+    return accessOptions
-+  }
-+
-+  private func areBiometricsEnabled() -> Bool {
-+    #if os(tvOS)
-+    return false
-+    #else
-+    return LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: nil)
-+    #endif
-+  }
-+
-+  private func get(with key: String, options: SecureStoreOptions) throws -> String?
-+  {
-     guard let key = validate(for: key) else {
-       throw InvalidKeyException()
-     }
-@@ -89,7 +205,7 @@ public final class SecureStoreModule: Module {
-   }
-
-   private func set(value: String, with key: String, options: SecureStoreOptions) throws -> Bool {
--    var setItemQuery = query(with: key, options: options, requireAuthentication: options.requireAuthentication)
-+    var setItemQuery = try query(with: key, options: options, requireAuthentication: options.requireAuthentication)
-
-     let valueData = value.data(using: .utf8)
-     setItemQuery[kSecValueData as String] = valueData
-@@ -98,17 +214,6 @@ public final class SecureStoreModule: Module {
-
-     if !options.requireAuthentication {
-       setItemQuery[kSecAttrAccessible as String] = accessibility
--    } else {
--      guard let _ = Bundle.main.infoDictionary?["NSFaceIDUsageDescription"] as? String else {
--        throw MissingPlistKeyException()
--      }
--
--      var error: Unmanaged<CFError>? = nil
--      guard let accessOptions = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility, .biometryCurrentSet, &error) else {
--        let errorCode = error.map { CFErrorGetCode($0.takeRetainedValue()) }
--        throw SecAccessControlError(errorCode)
--      }
--      setItemQuery[kSecAttrAccessControl as String] = accessOptions
-     }
-
-     let status = SecItemAdd(setItemQuery as CFDictionary, nil)
-@@ -116,10 +221,13 @@ public final class SecureStoreModule: Module {
-     switch status {
-     case errSecSuccess:
-       // On success we want to remove the other key alias and legacy key (if they exist) to avoid conflicts during reads
--      SecItemDelete(query(with: key, options: options) as CFDictionary)
--      SecItemDelete(query(with: key, options: options, requireAuthentication: !options.requireAuthentication) as CFDictionary)
-+      SecItemDelete(try query(with: key, options: options) as CFDictionary)
-+      SecItemDelete(try query(with: key, options: options,requireAuthentication: !options.requireAuthentication) as CFDictionary)
-       return true
-     case errSecDuplicateItem:
-+      if options.failOnDuplicate {
-+        throw SecureStoreRuntimeError("Key already exists")
-+      }
-       return try update(value: value, with: key, options: options)
-     default:
-       throw KeyChainException(status)
-@@ -127,15 +235,11 @@ public final class SecureStoreModule: Module {
-   }
-
-   private func update(value: String, with key: String, options: SecureStoreOptions) throws -> Bool {
--    var query = query(with: key, options: options, requireAuthentication: options.requireAuthentication)
-+    let query = try query(with: key, options: options, requireAuthentication: options.requireAuthentication)
-
-     let valueData = value.data(using: .utf8)
-     let updateDictionary = [kSecValueData as String: valueData]
-
--    if let authPrompt = options.authenticationPrompt {
--      query[kSecUseOperationPrompt as String] = authPrompt
--    }
--
-     let status = SecItemUpdate(query as CFDictionary, updateDictionary as CFDictionary)
-
-     if status == errSecSuccess {
-@@ -146,15 +250,11 @@ public final class SecureStoreModule: Module {
-   }
-
-   private func searchKeyChain(with key: String, options: SecureStoreOptions, requireAuthentication: Bool? = nil) throws -> Data? {
--    var query = query(with: key, options: options, requireAuthentication: requireAuthentication)
-+    var query = try query(with: key, options: options, requireAuthentication: requireAuthentication)
-
-     query[kSecMatchLimit as String] = kSecMatchLimitOne
-     query[kSecReturnData as String] = kCFBooleanTrue
-
--    if let authPrompt = options.authenticationPrompt {
--      query[kSecUseOperationPrompt as String] = authPrompt
--    }
--
-     var item: CFTypeRef?
-     let status = SecItemCopyMatching(query as CFDictionary, &item)
-
-@@ -171,21 +271,28 @@ public final class SecureStoreModule: Module {
-     }
-   }
-
--  private func query(with key: String, options: SecureStoreOptions, requireAuthentication: Bool? = nil) -> [String: Any] {
-+  private func query(with key: String, options: SecureStoreOptions, requireAuthentication: Bool? = nil) throws -> [String: Any] {
-     var service = options.keychainService ?? "app"
-     if let requireAuthentication {
-       service.append(":\(requireAuthentication ? "auth" : "no-auth")")
-     }
-
-     let encodedKey = Data(key.utf8)
-+    let accessibility = attributeWith(options: options)
-
-     var query: [String: Any] = [
-       kSecClass as String: kSecClassGenericPassword,
-       kSecAttrService as String: service,
-       kSecAttrGeneric as String: encodedKey,
--      kSecAttrAccount as String: encodedKey
-+      kSecAttrAccount as String: encodedKey,
-     ]
-
-+    if options.requireAuthentication {
-+      query[kSecAttrAccessControl as String] = try getAccessControl(
-+        accessibility: accessibility
-+      )
-+    }
-+
-     if let accessGroup = options.accessGroup {
-       query[kSecAttrAccessGroup as String] = accessGroup
-     }
-diff --git a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
-index 7e3fa4d..481b1fe 100644
---- a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
-+++ b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
-@@ -15,4 +15,13 @@ internal struct SecureStoreOptions: Record {
-
-   @Field
-   var accessGroup: String?
-+
-+  @Field
-+  var failOnDuplicate: Bool = false
-+
-+  @Field
-+  var alwaysAskForAuth: Bool = false
-+
-+  @Field
-+  var askForAuthOnSave: Bool = false
- }
-diff --git a/node_modules/expo-secure-store/src/SecureStore.ts b/node_modules/expo-secure-store/src/SecureStore.ts
-index ee43e04..ccfd2a9 100644
---- a/node_modules/expo-secure-store/src/SecureStore.ts
-+++ b/node_modules/expo-secure-store/src/SecureStore.ts
-@@ -3,6 +3,54 @@ import { byteCountOverLimit, VALUE_BYTES_LIMIT } from './byteCounter';
-
- export type KeychainAccessibilityConstant = number;
-
-+/**
-+ * Authentication type returned by the SecureStore after reading item or saving it to the store.
-+ */
-+export const AUTH_TYPE = {
-+  /**
-+   * This is purely for backwards compatibility.
-+   * Although it is not listed as a return value of the getAuthenticationType() method,
-+   * it is still present in the Android code.
-+   * @see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.AuthenticationResult#getAuthenticationType()
-+   * @see https://developer.android.com/reference/androidx/biometric/BiometricPrompt#AUTHENTICATION_RESULT_TYPE_UNKNOWN()
-+   * @platform android
-+   */
-+  UNKNOWN: -1,
-+  /**
-+   * Returned when the authentication fails
-+   * @platform android
-+   * @platform ios
-+   */
-+  NONE: 0,
-+  /**
-+   * Generic type, not specified whether it was a passcode or pattern.
-+   * @platform android
-+   * @platform ios
-+   */
-+  CREDENTIALS: 1,
-+  /**
-+   * Generic type, not specified whether it was a face scan or a fingerprint
-+   * @platform android
-+   */
-+  BIOMETRICS: 2,
-+  /**
-+   * FaceID was used to authenticate
-+   * @platform ios
-+   */
-+  FACE_ID: 3,
-+  /**
-+   * TouchID was used to authenticate
-+   * @platform ios
-+   */
-+  TOUCH_ID: 4,
-+  /**
-+   * OpticID was used to authenticate (reserved by apple, used on Apple Vision Pro, not iOS)
-+   */
-+  OPTIC_ID: 5
-+} as const;
-+
-+type AuthType = (typeof AUTH_TYPE)[keyof typeof AUTH_TYPE];
-+
- // @needsAudit
- /**
-  * The data in the keychain item cannot be accessed after a restart until the device has been
-@@ -102,6 +150,45 @@ export type SecureStoreOptions = {
-    * @platform ios
-    */
-   accessGroup?: string;
-+
-+  /**
-+   * If value already exists, throw an error
-+   *
-+   * On Android it throws an error before the prompt for authentication.
-+   * On iOS it throws an error after authentication.
-+   */
-+  failOnDuplicate?: boolean;
-+  /**
-+   * On iOS, the system sometimes skips authentication if it has been done recently.
-+   * This also applies to actions such as unlocking the screen.
-+   * Setting this flag to true will ensure that authentication is always required before accessing the store.
-+   *
-+   * Use this wisely, when the user did not authenticated recently, setting this to true will lead to prompt
-+   * being displayed twice when accessing the store value.
-+   * The best use-case for this flag is when testing the behavior on iOS simulator,
-+   * since the auth there may not be prompted at all without this flag.
-+   *
-+   * @warning This flag works only for async version of the save/read SecureStore methods.
-+   * @platform ios
-+   */
-+  alwaysAskForAuth?: boolean;
-+  /**
-+   * On iOS, the system does not ask for auth when saving the value to the SecureStore.
-+   * The Android however, displays the authentication prompt when saving the value.
-+   * To keep the behavior on every platform similar as much as possible,
-+   * setting this flag to true will ensure that authentication is required when saving a value to the store.
-+   *
-+   * @warning This flag works only for async version of the save/read SecureStore methods.
-+   * @platform ios
-+   */
-+  askForAuthOnSave?: boolean;
-+  /**
-+   * On Android, there is often no fallback option if biometrics fail.
-+   * Therefore, we may want to allow the user to authenticate using credentials if they prefer to.
-+   *
-+   * @platform android
-+   */
-+  enableCredentialsAlternative?: boolean;
- };
-
- // @needsAudit
-@@ -151,7 +238,7 @@ export async function deleteItemAsync(
- export async function getItemAsync(
-   key: string,
-   options: SecureStoreOptions = {}
--): Promise<string | null> {
-+): Promise<[string | null, AuthType]> {
-   ensureValidKey(key);
-   return await ExpoSecureStore.getValueWithKeyAsync(key, options);
- }
-@@ -170,15 +257,15 @@ export async function setItemAsync(
-   key: string,
-   value: string,
-   options: SecureStoreOptions = {}
--): Promise<void> {
-+): Promise<AuthType> {
-   ensureValidKey(key);
-   if (!isValidValue(value)) {
-     throw new Error(
--      `Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.`
-+      `Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.`,
-     );
-   }
-
--  await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
-+  return await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
- }
-
- /**
-@@ -190,7 +277,7 @@ export async function setItemAsync(
-  * @param options An [`SecureStoreOptions`](#securestoreoptions) object.
-  *
-  */
--export function setItem(key: string, value: string, options: SecureStoreOptions = {}): void {
-+export function setItem(key: string, value: string, options: SecureStoreOptions = {}): AuthType {
-   ensureValidKey(key);
-   if (!isValidValue(value)) {
-     throw new Error(
-@@ -211,7 +298,7 @@ export function setItem(key: string, value: string, options: SecureStoreOptions
-  * @return Previously stored value. It resolves with `null` if there is no entry
-  * for the given key or if the key has been invalidated.
-  */
--export function getItem(key: string, options: SecureStoreOptions = {}): string | null {
-+export function getItem(key: string, options: SecureStoreOptions = {}): [string | null, AuthType] {
-   ensureValidKey(key);
-   return ExpoSecureStore.getValueWithKeySync(key, options);
- }
diff --git a/patches/expo-secure-store/expo-secure-store+14.2.4+001+enable-device-fallback.patch b/patches/expo-secure-store/expo-secure-store+14.2.4+001+enable-device-fallback.patch
new file mode 100644
index 000000000000..d5d5edad1a25
--- /dev/null
+++ b/patches/expo-secure-store/expo-secure-store+14.2.4+001+enable-device-fallback.patch
@@ -0,0 +1,435 @@
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
+index a281b88..4a1a009 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
+@@ -2,6 +2,7 @@ package expo.modules.securestore
+
+ import android.annotation.SuppressLint
+ import android.app.Activity
++import android.app.KeyguardManager
+ import android.content.Context
+ import android.os.Build
+ import androidx.biometric.BiometricManager
+@@ -19,9 +20,9 @@ class AuthenticationHelper(
+ ) {
+   private var isAuthenticating = false
+
+-  suspend fun authenticateCipher(cipher: Cipher, requiresAuthentication: Boolean, title: String): Cipher {
++  suspend fun authenticateCipher(cipher: Cipher, requiresAuthentication: Boolean, title: String, enableDeviceFallback: Boolean): Cipher {
+     if (requiresAuthentication) {
+-      return openAuthenticationPrompt(cipher, title).cryptoObject?.cipher
++      return openAuthenticationPrompt(cipher, title, enableDeviceFallback).cryptoObject?.cipher
+         ?: throw AuthenticationException("Couldn't get cipher from authentication result")
+     }
+     return cipher
+@@ -29,7 +30,8 @@ class AuthenticationHelper(
+
+   private suspend fun openAuthenticationPrompt(
+     cipher: Cipher,
+-    title: String
++    title: String,
++    enableDeviceFallback: Boolean
+   ): BiometricPrompt.AuthenticationResult {
+     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+       throw AuthenticationException("Biometric authentication requires Android API 23")
+@@ -41,11 +43,16 @@ class AuthenticationHelper(
+     isAuthenticating = true
+
+     try {
+-      assertBiometricsSupport()
++      if (enableDeviceFallback) {
++        assertDeviceSecurity()
++      } else {
++        assertBiometricsSupport()
++      }
++
+       val fragmentActivity = getCurrentActivity() as? FragmentActivity
+         ?: throw AuthenticationException("Cannot display biometric prompt when the app is not in the foreground")
+
+-      val authenticationPrompt = AuthenticationPrompt(fragmentActivity, context, title)
++      val authenticationPrompt = AuthenticationPrompt(fragmentActivity, context, title, enableDeviceFallback)
+
+       return withContext(Dispatchers.Main.immediate) {
+         return@withContext authenticationPrompt.authenticate(cipher)
+@@ -56,6 +63,14 @@ class AuthenticationHelper(
+     }
+   }
+
++  fun assertDeviceSecurity() {
++    val manager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
++    val isSecure = manager.isDeviceSecure
++    if (!isSecure) {
++      throw AuthenticationException("No authentication method available")
++    }
++  }
++
+   fun assertBiometricsSupport() {
+     val biometricManager = BiometricManager.from(context)
+     @SuppressLint("SwitchIntDef") // BiometricManager.BIOMETRIC_SUCCESS shouldn't do anything
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt
+index e5729cc..ce448d6 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationPrompt.kt
+@@ -1,5 +1,7 @@
+ package expo.modules.securestore
+
++import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
++import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ import android.content.Context
+ import androidx.biometric.BiometricPrompt
+ import androidx.biometric.BiometricPrompt.PromptInfo
+@@ -11,12 +13,23 @@ import kotlin.coroutines.resume
+ import kotlin.coroutines.resumeWithException
+ import kotlin.coroutines.suspendCoroutine
+
+-class AuthenticationPrompt(private val currentActivity: FragmentActivity, context: Context, title: String) {
++class AuthenticationPrompt(private val currentActivity: FragmentActivity, context: Context, title: String, enableDeviceFallback: Boolean) {
++  private var authType: Int = if (enableDeviceFallback) BIOMETRIC_STRONG or DEVICE_CREDENTIAL else BIOMETRIC_STRONG
+   private var executor: Executor = ContextCompat.getMainExecutor(context)
+-  private var promptInfo = PromptInfo.Builder()
+-    .setTitle(title)
+-    .setNegativeButtonText(context.getString(android.R.string.cancel))
+-    .build()
++  private var promptInfo = buildPromptInfo(context, title, enableDeviceFallback)
++
++  private fun buildPromptInfo(context: Context, title: String, enableDeviceFallback: Boolean): PromptInfo {
++    var prompt = PromptInfo.Builder()
++      .setTitle(title)
++      .setAllowedAuthenticators(authType)
++
++    if (!enableDeviceFallback) {
++      prompt = prompt.
++        setNegativeButtonText(context.getString(android.R.string.cancel))
++    }
++
++    return prompt.build()
++  }
+
+   suspend fun authenticate(cipher: Cipher): BiometricPrompt.AuthenticationResult? =
+     suspendCoroutine { continuation ->
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
+index 0fef884..bf9689b 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
+@@ -75,6 +75,15 @@ open class SecureStoreModule : Module() {
+       }
+     }
+
++    Function("canUseDeviceCredentialsAuthentication") {
++      return@Function try {
++        authenticationHelper.assertDeviceSecurity()
++        true
++      } catch (e: AuthenticationException) {
++        false
++      }
++    }
++
+     OnCreate {
+       authenticationHelper = AuthenticationHelper(reactContext, appContext.legacyModuleRegistry)
+       hybridAESEncryptor = HybridAESEncryptor(reactContext, mAESEncryptor)
+@@ -201,7 +210,7 @@ open class SecureStoreModule : Module() {
+        back a value.
+        */
+       val secretKeyEntry: SecretKeyEntry = getOrCreateKeyEntry(SecretKeyEntry::class.java, mAESEncryptor, options, options.requireAuthentication)
+-      val encryptedItem = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper)
++      val encryptedItem = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper, options.enableDeviceFallback)
+       encryptedItem.put(SCHEME_PROPERTY, AESEncryptor.NAME)
+       saveEncryptedItem(encryptedItem, prefs, keychainAwareKey, options.requireAuthentication, options.keychainService)
+
+@@ -343,7 +352,11 @@ open class SecureStoreModule : Module() {
+     return getKeyEntry(keyStoreEntryClass, encryptor, options, requireAuthentication) ?: run {
+       // Android won't allow us to generate the keys if the device doesn't support biometrics or no biometrics are enrolled
+       if (requireAuthentication) {
+-        authenticationHelper.assertBiometricsSupport()
++        if (options.enableDeviceFallback) {
++          authenticationHelper.assertDeviceSecurity()
++        } else {
++          authenticationHelper.assertBiometricsSupport()
++        }
+       }
+       encryptor.initializeKeyStoreEntry(keyStore, options)
+     }
+@@ -383,6 +396,7 @@ open class SecureStoreModule : Module() {
+     private const val KEYSTORE_ALIAS_PROPERTY = "keystoreAlias"
+     const val USES_KEYSTORE_SUFFIX_PROPERTY = "usesKeystoreSuffix"
+     const val DEFAULT_KEYSTORE_ALIAS = "key_v1"
++    const val DEFAULT_FALLBACK_KEYSTORE_ALIAS = "fallback_key_v1"
+     const val AUTHENTICATED_KEYSTORE_SUFFIX = "keystoreAuthenticated"
+     const val UNAUTHENTICATED_KEYSTORE_SUFFIX = "keystoreUnauthenticated"
+   }
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
+index 79a600f..0455fd6 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
+@@ -8,5 +8,6 @@ class SecureStoreOptions(
+   // Prompt can't be an empty string
+   @Field var authenticationPrompt: String = " ",
+   @Field var keychainService: String = SecureStoreModule.DEFAULT_KEYSTORE_ALIAS,
+-  @Field var requireAuthentication: Boolean = false
++  @Field var requireAuthentication: Boolean = false,
++  @Field var enableDeviceFallback: Boolean = false
+ ) : Record, Serializable
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
+index 3a12dc9..1cec6c0 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
+@@ -1,9 +1,10 @@
+ package expo.modules.securestore.encryptors
+
+-import android.annotation.TargetApi
++import android.os.Build
+ import android.security.keystore.KeyGenParameterSpec
+ import android.security.keystore.KeyProperties
+ import android.util.Base64
++import androidx.annotation.RequiresApi
+ import expo.modules.securestore.AuthenticationHelper
+ import expo.modules.securestore.DecryptException
+ import expo.modules.securestore.SecureStoreModule
+@@ -33,6 +34,11 @@ import javax.crypto.spec.GCMParameterSpec
+ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
+   override fun getKeyStoreAlias(options: SecureStoreOptions): String {
+     val baseAlias = options.keychainService
++
++    if (baseAlias == SecureStoreModule.DEFAULT_KEYSTORE_ALIAS && options.enableDeviceFallback) {
++      return "$AES_CIPHER:${SecureStoreModule.DEFAULT_FALLBACK_KEYSTORE_ALIAS}"
++    }
++
+     return "$AES_CIPHER:$baseAlias"
+   }
+
+@@ -50,17 +56,22 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
+     return "${getKeyStoreAlias(options)}:$suffix"
+   }
+
+-  @TargetApi(23)
++  @RequiresApi(Build.VERSION_CODES.R)
+   @Throws(GeneralSecurityException::class)
+   override fun initializeKeyStoreEntry(keyStore: KeyStore, options: SecureStoreOptions): KeyStore.SecretKeyEntry {
+     val extendedKeystoreAlias = getExtendedKeyStoreAlias(options, options.requireAuthentication)
+     val keyPurposes = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
+
++    val authType =
++      if (options.enableDeviceFallback) KeyProperties.AUTH_BIOMETRIC_STRONG or KeyProperties.AUTH_DEVICE_CREDENTIAL
++      else KeyProperties.AUTH_BIOMETRIC_STRONG
++
+     val algorithmSpec: AlgorithmParameterSpec = KeyGenParameterSpec.Builder(extendedKeystoreAlias, keyPurposes)
+       .setKeySize(AES_KEY_SIZE_BITS)
+       .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+       .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+       .setUserAuthenticationRequired(options.requireAuthentication)
++      .setUserAuthenticationParameters(0, authType)
+       .build()
+
+     val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, keyStore.provider)
+@@ -78,14 +89,15 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
+     keyStoreEntry: KeyStore.SecretKeyEntry,
+     requireAuthentication: Boolean,
+     authenticationPrompt: String,
+-    authenticationHelper: AuthenticationHelper
++    authenticationHelper: AuthenticationHelper,
++    enableDeviceFallback: Boolean,
+   ): JSONObject {
+     val secretKey = keyStoreEntry.secretKey
+     val cipher = Cipher.getInstance(AES_CIPHER)
+     cipher.init(Cipher.ENCRYPT_MODE, secretKey)
+
+     val gcmSpec = cipher.parameters.getParameterSpec(GCMParameterSpec::class.java)
+-    val authenticatedCipher = authenticationHelper.authenticateCipher(cipher, requireAuthentication, authenticationPrompt)
++    val authenticatedCipher = authenticationHelper.authenticateCipher(cipher, requireAuthentication, authenticationPrompt, enableDeviceFallback)
+
+     return createEncryptedItemWithCipher(plaintextValue, authenticatedCipher, gcmSpec)
+   }
+@@ -128,7 +140,7 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
+       throw DecryptException("Authentication tag length must be at least $MIN_GCM_AUTHENTICATION_TAG_LENGTH bits long", key, options.keychainService)
+     }
+     cipher.init(Cipher.DECRYPT_MODE, keyStoreEntry.secretKey, gcmSpec)
+-    val unlockedCipher = authenticationHelper.authenticateCipher(cipher, requiresAuthentication, options.authenticationPrompt)
++    val unlockedCipher = authenticationHelper.authenticateCipher(cipher, requiresAuthentication, options.authenticationPrompt, options.enableDeviceFallback)
+     return String(unlockedCipher.doFinal(ciphertextBytes), StandardCharsets.UTF_8)
+   }
+
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
+index fb42599..e996b39 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
+@@ -51,6 +51,11 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
+
+   override fun getKeyStoreAlias(options: SecureStoreOptions): String {
+     val baseAlias = options.keychainService
++
++    if (baseAlias == SecureStoreModule.DEFAULT_KEYSTORE_ALIAS && options.enableDeviceFallback) {
++      return "$RSA_CIPHER:${SecureStoreModule.DEFAULT_FALLBACK_KEYSTORE_ALIAS}"
++    }
++
+     return "$RSA_CIPHER:$baseAlias"
+   }
+
+@@ -69,7 +74,8 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
+     keyStoreEntry: KeyStore.PrivateKeyEntry,
+     requireAuthentication: Boolean,
+     authenticationPrompt: String,
+-    authenticationHelper: AuthenticationHelper
++    authenticationHelper: AuthenticationHelper,
++    enableDeviceFallback: Boolean,
+   ): JSONObject {
+     // This should never be called after we dropped Android SDK 22 support.
+     throw EncryptException(
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
+index e493467..39459ff 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
+@@ -25,7 +25,8 @@ interface KeyBasedEncryptor<E : KeyStore.Entry> {
+     keyStoreEntry: E,
+     requireAuthentication: Boolean,
+     authenticationPrompt: String,
+-    authenticationHelper: AuthenticationHelper
++    authenticationHelper: AuthenticationHelper,
++    enableDeviceFallback: Boolean,
+   ): JSONObject
+
+   @Throws(GeneralSecurityException::class, JSONException::class)
+diff --git a/node_modules/expo-secure-store/build/SecureStore.d.ts b/node_modules/expo-secure-store/build/SecureStore.d.ts
+index d5cd157..835a4d4 100644
+--- a/node_modules/expo-secure-store/build/SecureStore.d.ts
++++ b/node_modules/expo-secure-store/build/SecureStore.d.ts
+@@ -78,6 +78,16 @@ export type SecureStoreOptions = {
+      * @platform ios
+      */
+     accessGroup?: string;
++    /**
++     * This flag enables users to authenticate using Lock Screen Knowledge Factor (e.g. PIN, pattern or password).
++     * For sensitive apps, it is recommended not having biometric fall back to such factor.
++     * @see: https://developer.android.com/security/fraud-prevention/authentication
++     *
++     * @default false
++     * @platform android
++     * @platform ios
++     */
++    enableDeviceFallback?: boolean;
+ };
+ /**
+  * Returns whether the SecureStore API is enabled on the current device. This does not check the app
+@@ -148,4 +158,11 @@ export declare function getItem(key: string, options?: SecureStoreOptions): stri
+  * @platform ios
+  */
+ export declare function canUseBiometricAuthentication(): boolean;
++/**
++ * Checks whether any device credentials are configured on the device.
++ * @return `true` if the device has device credentials configured. Otherwise, returns `false`.
++ * @platform android
++ * @platform ios
++ */
++export declare function canUseDeviceCredentialsAuthentication(): boolean;
+ //# sourceMappingURL=SecureStore.d.ts.map
+\ No newline at end of file
+diff --git a/node_modules/expo-secure-store/build/SecureStore.js b/node_modules/expo-secure-store/build/SecureStore.js
+index 4d87b38..e11bd94 100644
+--- a/node_modules/expo-secure-store/build/SecureStore.js
++++ b/node_modules/expo-secure-store/build/SecureStore.js
+@@ -143,6 +143,15 @@ export function getItem(key, options = {}) {
+ export function canUseBiometricAuthentication() {
+     return ExpoSecureStore.canUseBiometricAuthentication();
+ }
++/**
++ * Checks whether any device credentials are configured on the device.
++ * @return `true` if the device has device credentials configured. Otherwise, returns `false`.
++ * @platform android
++ * @platform ios
++ */
++export function canUseDeviceCredentialsAuthentication() {
++    return ExpoSecureStore.canUseDeviceCredentialsAuthentication();
++}
+ function ensureValidKey(key) {
+     if (!isValidKey(key)) {
+         throw new Error(`Invalid key provided to SecureStore. Keys must not be empty and contain only alphanumeric characters, ".", "-", and "_".`);
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreModule.swift b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+index 439b08d..1268979 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreModule.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+@@ -66,6 +66,14 @@ public final class SecureStoreModule: Module {
+       return isBiometricsSupported
+       #endif
+     }
++
++    Function("canUseDeviceCredentialsAuthentication") { () -> Bool in
++      return areDeviceCredentialsEnabled()
++    }
++  }
++
++  private func areDeviceCredentialsEnabled() -> Bool {
++    return LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: nil)
+   }
+
+   private func get(with key: String, options: SecureStoreOptions) throws -> String? {
+@@ -99,12 +107,17 @@ public final class SecureStoreModule: Module {
+     if !options.requireAuthentication {
+       setItemQuery[kSecAttrAccessible as String] = accessibility
+     } else {
+-      guard let _ = Bundle.main.infoDictionary?["NSFaceIDUsageDescription"] as? String else {
+-        throw MissingPlistKeyException()
++      if (!options.enableDeviceFallback) {
++        guard let _ = Bundle.main.infoDictionary?["NSFaceIDUsageDescription"] as? String else {
++          throw MissingPlistKeyException()
++        }
+       }
+
+       var error: Unmanaged<CFError>? = nil
+-      guard let accessOptions = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility, .biometryCurrentSet, &error) else {
++
++      let accessControlFlag: SecAccessControlCreateFlags = options.enableDeviceFallback ? .userPresence : .biometryCurrentSet
++
++      guard let accessOptions = SecAccessControlCreateWithFlags(kCFAllocatorDefault, accessibility, accessControlFlag, &error) else {
+         let errorCode = error.map { CFErrorGetCode($0.takeRetainedValue()) }
+         throw SecAccessControlError(errorCode)
+       }
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+index 7e3fa4d..c9e843b 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+@@ -15,4 +15,7 @@ internal struct SecureStoreOptions: Record {
+
+   @Field
+   var accessGroup: String?
++
++  @Field
++  var enableDeviceFallback: Bool = false
+ }
+diff --git a/node_modules/expo-secure-store/src/SecureStore.ts b/node_modules/expo-secure-store/src/SecureStore.ts
+index ee43e04..4f253a1 100644
+--- a/node_modules/expo-secure-store/src/SecureStore.ts
++++ b/node_modules/expo-secure-store/src/SecureStore.ts
+@@ -102,6 +102,17 @@ export type SecureStoreOptions = {
+    * @platform ios
+    */
+   accessGroup?: string;
++
++  /**
++   * This flag enables users to authenticate using Lock Screen Knowledge Factor (e.g. PIN, pattern or password).
++   * For sensitive apps, it is recommended not having biometric fall back to such factor.
++   * @see: https://developer.android.com/security/fraud-prevention/authentication
++   *
++   * @default false
++   * @platform android
++   * @platform ios
++   */
++  enableDeviceFallback?: boolean;
+ };
+
+ // @needsAudit
+@@ -226,6 +237,16 @@ export function canUseBiometricAuthentication(): boolean {
+   return ExpoSecureStore.canUseBiometricAuthentication();
+ }
+
++/**
++ * Checks whether any device credentials are configured on the device.
++ * @return `true` if the device has device credentials configured. Otherwise, returns `false`.
++ * @platform android
++ * @platform ios
++ */
++export function canUseDeviceCredentialsAuthentication(): boolean {
++  return ExpoSecureStore.canUseDeviceCredentialsAuthentication();
++}
++
+ function ensureValidKey(key: string) {
+   if (!isValidKey(key)) {
+     throw new Error(
diff --git a/patches/expo-secure-store/expo-secure-store+14.2.4+002+return-auth-type.patch b/patches/expo-secure-store/expo-secure-store+14.2.4+002+return-auth-type.patch
new file mode 100644
index 000000000000..c8cdfcf5ac02
--- /dev/null
+++ b/patches/expo-secure-store/expo-secure-store+14.2.4+002+return-auth-type.patch
@@ -0,0 +1,901 @@
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
+index 4a1a009..980ef0a 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/AuthenticationHelper.kt
+@@ -20,12 +20,12 @@ class AuthenticationHelper(
+ ) {
+   private var isAuthenticating = false
+
+-  suspend fun authenticateCipher(cipher: Cipher, requiresAuthentication: Boolean, title: String, enableDeviceFallback: Boolean): Cipher {
+-    if (requiresAuthentication) {
+-      return openAuthenticationPrompt(cipher, title, enableDeviceFallback).cryptoObject?.cipher
+-        ?: throw AuthenticationException("Couldn't get cipher from authentication result")
++  suspend fun authenticateCipher(cipher: Cipher, title: String, enableDeviceFallback: Boolean): BiometricPrompt.AuthenticationResult {
++    val promptResult = openAuthenticationPrompt(cipher, title, enableDeviceFallback)
++    if (promptResult.cryptoObject?.cipher == null) {
++      throw AuthenticationException("Couldn't get cipher from authentication result")
+     }
+-    return cipher
++    return promptResult
+   }
+
+   private suspend fun openAuthenticationPrompt(
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
+index 3f21552..d209efc 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
+@@ -36,23 +36,23 @@ open class SecureStoreModule : Module() {
+
+     AsyncFunction("setValueWithKeyAsync") Coroutine { value: String?, key: String?, options: SecureStoreOptions ->
+       key ?: throw NullKeyException()
+-      return@Coroutine setItemImpl(key, value, options, false)
++      return@Coroutine narrowSecureStoreFeedback(SecureStoreFeedbackAction.SET, setItemImpl(key, value, options, false), options).value
+     }
+
+     AsyncFunction("getValueWithKeyAsync") Coroutine { key: String, options: SecureStoreOptions ->
+-      return@Coroutine getItemImpl(key, options)
++      return@Coroutine narrowSecureStoreFeedback(SecureStoreFeedbackAction.GET,getItemImpl(key, options), options).value
+     }
+
+     Function("setValueWithKeySync") { value: String?, key: String?, options: SecureStoreOptions ->
+       key ?: throw NullKeyException()
+       return@Function runBlocking {
+-        return@runBlocking setItemImpl(key, value, options, keyIsInvalidated = false)
++        return@runBlocking narrowSecureStoreFeedback(SecureStoreFeedbackAction.SET, setItemImpl(key, value, options, keyIsInvalidated = false), options).value
+       }
+     }
+
+     Function("getValueWithKeySync") { key: String, options: SecureStoreOptions ->
+       return@Function runBlocking {
+-        return@runBlocking getItemImpl(key, options)
++        return@runBlocking narrowSecureStoreFeedback(SecureStoreFeedbackAction.GET, getItemImpl(key, options), options).value
+       }
+     }
+
+@@ -94,7 +94,23 @@ open class SecureStoreModule : Module() {
+     }
+   }
+
+-  private suspend fun getItemImpl(key: String, options: SecureStoreOptions): String? {
++  private suspend fun <T> narrowSecureStoreFeedback(action: String, feedback: SecureStoreOriginalFeedback<T>, options: SecureStoreOptions): SecureStoreNarrowedFeedback<T> {
++    if (!options.returnUsedAuthenticationType) {
++      return feedback
++    }
++
++    if (action == SecureStoreFeedbackAction.GET) {
++      return SecureStoreGetFeedback(feedback.source, feedback.authenticationResult)
++    }
++
++    if (action == SecureStoreFeedbackAction.SET) {
++      return SecureStoreSetFeedback(feedback.source, feedback.authenticationResult)
++    }
++
++    return feedback
++  }
++
++  private suspend fun getItemImpl(key: String, options: SecureStoreOptions): SecureStoreOriginalFeedback<out String?> {
+     // We use a SecureStore-specific shared preferences file, which lets us do things like enumerate
+     // its entries or clear all of them
+     val prefs: SharedPreferences = getSharedPreferences()
+@@ -104,10 +120,10 @@ open class SecureStoreModule : Module() {
+     } else if (prefs.contains(key)) { // For backwards-compatibility try to read using the old key format
+       return readJSONEncodedItem(key, prefs, options)
+     }
+-    return null
++    return SecureStoreOriginalFeedback(null)
+   }
+
+-  private suspend fun readJSONEncodedItem(key: String, prefs: SharedPreferences, options: SecureStoreOptions): String? {
++  private suspend fun readJSONEncodedItem(key: String, prefs: SharedPreferences, options: SecureStoreOptions): SecureStoreOriginalFeedback<out String?> {
+     val keychainAwareKey = createKeychainAwareKey(key, options.keychainService)
+
+     val legacyEncryptedItemString = prefs.getString(key, null)
+@@ -126,7 +142,7 @@ open class SecureStoreModule : Module() {
+       ""
+     }
+
+-    encryptedItemString ?: return null
++    encryptedItemString ?: return SecureStoreOriginalFeedback(null)
+
+     val encryptedItem: JSONObject = try {
+       JSONObject(encryptedItemString)
+@@ -149,13 +165,13 @@ open class SecureStoreModule : Module() {
+                 "This situation occurs when the app is reinstalled. The value will be removed to avoid future errors. Returning null"
+             )
+             deleteItemImpl(key, options)
+-            return null
++            return SecureStoreOriginalFeedback(null)
+           }
+           return mAESEncryptor.decryptItem(key, encryptedItem, secretKeyEntry, options, authenticationHelper)
+         }
+         HybridAESEncryptor.NAME -> {
+           val privateKeyEntry = getKeyEntryCompat(PrivateKeyEntry::class.java, hybridAESEncryptor, options, requireAuthentication, usesKeystoreSuffix)
+-            ?: return null
++            ?: return SecureStoreOriginalFeedback(null)
+           return hybridAESEncryptor.decryptItem(key, encryptedItem, privateKeyEntry, options, authenticationHelper)
+         }
+         else -> {
+@@ -164,7 +180,7 @@ open class SecureStoreModule : Module() {
+       }
+     } catch (e: KeyPermanentlyInvalidatedException) {
+       Log.w(TAG, "The requested key has been permanently invalidated. Returning null")
+-      return null
++      return SecureStoreOriginalFeedback(null)
+     } catch (e: BadPaddingException) {
+       // The key from the KeyStore is unable to decode the entry. This is because a new key was generated, but the entries are encrypted using the old one.
+       // This usually means that the user has reinstalled the app. We can safely remove the old value and return null as it's impossible to decrypt it.
+@@ -174,7 +190,7 @@ open class SecureStoreModule : Module() {
+           "The entry in shared preferences is out of sync with the keystore. It will be removed, returning null."
+       )
+       deleteItemImpl(key, options)
+-      return null
++      return SecureStoreOriginalFeedback(null)
+     } catch (e: GeneralSecurityException) {
+       throw (DecryptException(e.message, key, options.keychainService, e))
+     } catch (e: CodedException) {
+@@ -184,7 +200,7 @@ open class SecureStoreModule : Module() {
+     }
+   }
+
+-  private suspend fun setItemImpl(key: String, value: String?, options: SecureStoreOptions, keyIsInvalidated: Boolean) {
++  private suspend fun setItemImpl(key: String, value: String?, options: SecureStoreOptions, keyIsInvalidated: Boolean): SecureStoreOriginalFeedback<out JSONObject?> {
+     val keychainAwareKey = createKeychainAwareKey(key, options.keychainService)
+     val prefs: SharedPreferences = getSharedPreferences()
+
+@@ -193,7 +209,7 @@ open class SecureStoreModule : Module() {
+       if (!success) {
+         throw WriteException("Could not write a null value to SecureStore", key, options.keychainService)
+       }
+-      return
++      return SecureStoreOriginalFeedback(null)
+     }
+
+     try {
+@@ -210,7 +226,8 @@ open class SecureStoreModule : Module() {
+        back a value.
+        */
+       val secretKeyEntry: SecretKeyEntry = getOrCreateKeyEntry(SecretKeyEntry::class.java, mAESEncryptor, options, options.requireAuthentication)
+-      val encryptedItem = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper, options.enableDeviceFallback)
++      val encryptResult = mAESEncryptor.createEncryptedItem(value, secretKeyEntry, options.requireAuthentication, options.authenticationPrompt, authenticationHelper, options.enableDeviceFallback)
++      val encryptedItem = encryptResult.value
+       encryptedItem.put(SCHEME_PROPERTY, AESEncryptor.NAME)
+       saveEncryptedItem(encryptedItem, prefs, keychainAwareKey, options.requireAuthentication, options.keychainService)
+
+@@ -218,6 +235,9 @@ open class SecureStoreModule : Module() {
+       if (prefs.contains(key)) {
+         prefs.edit().remove(key).apply()
+       }
++
++      return encryptResult
++
+     } catch (e: KeyPermanentlyInvalidatedException) {
+       if (!keyIsInvalidated) {
+         Log.w(TAG, "Key has been invalidated, retrying with the key deleted")
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
+index 0455fd6..4e30440 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
+@@ -1,5 +1,6 @@
+ package expo.modules.securestore
+
++import androidx.biometric.BiometricPrompt
+ import expo.modules.kotlin.records.Field
+ import expo.modules.kotlin.records.Record
+ import java.io.Serializable
+@@ -9,5 +10,58 @@ class SecureStoreOptions(
+   @Field var authenticationPrompt: String = " ",
+   @Field var keychainService: String = SecureStoreModule.DEFAULT_KEYSTORE_ALIAS,
+   @Field var requireAuthentication: Boolean = false,
+-  @Field var enableDeviceFallback: Boolean = false
++  @Field var enableDeviceFallback: Boolean = false,
++  @Field var returnUsedAuthenticationType: Boolean = false
+ ) : Record, Serializable
++
++enum class SecureStoreAuthType(index: Int) {
++  UNKNOWN(BiometricPrompt.AUTHENTICATION_RESULT_TYPE_UNKNOWN),
++  CREDENTIAL(BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL),
++  BIOMETRIC(BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC),
++
++  /** Prompt failed, no authentication was used at all */
++  NONE(0)
++}
++
++open class SecureStoreFeedbackAction {
++  companion object {
++    const val GET = "GET"
++    const val SET = "SET"
++  }
++}
++
++abstract class SecureStoreFeedback<T, R>(
++  val source: T,
++  val authenticationResult: BiometricPrompt.AuthenticationResult?
++) {
++  @Field var authType: SecureStoreAuthType = when (authenticationResult?.authenticationType) {
++    BiometricPrompt.AUTHENTICATION_RESULT_TYPE_UNKNOWN -> {
++      SecureStoreAuthType.UNKNOWN
++    }
++    BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL -> {
++      SecureStoreAuthType.CREDENTIAL
++    }
++    BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC -> {
++      SecureStoreAuthType.BIOMETRIC
++    }
++    else -> {
++      SecureStoreAuthType.NONE
++    }
++  }
++  abstract val value: R
++}
++
++class SecureStoreGetFeedback<T>(source: T, authenticationResult: BiometricPrompt.AuthenticationResult? = null): SecureStoreFeedback<T, Pair<T, Int>>(source, authenticationResult) {
++  /** Used to return easily convertible values to JS code */
++  @Field override var value: Pair<T, Int> = Pair(source, authType.ordinal)
++}
++
++class SecureStoreSetFeedback<T>(source: T, authenticationResult: BiometricPrompt.AuthenticationResult? = null): SecureStoreFeedback<T, Int>(source, authenticationResult) {
++  @Field override var value: Int = authType.ordinal
++}
++
++class SecureStoreOriginalFeedback<T>(source: T, authenticationResult: BiometricPrompt.AuthenticationResult? = null): SecureStoreFeedback<T, T>(source, authenticationResult) {
++  @Field override var value: T = source
++}
++
++typealias SecureStoreNarrowedFeedback<T> = SecureStoreFeedback<T, out Any?>
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
+index 1cd187f..d927ff1 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/AESEncryptor.kt
+@@ -5,10 +5,12 @@ import android.security.keystore.KeyGenParameterSpec
+ import android.security.keystore.KeyProperties
+ import android.util.Base64
+ import androidx.annotation.RequiresApi
++import androidx.biometric.BiometricPrompt
+ import expo.modules.securestore.AuthenticationHelper
+ import expo.modules.securestore.DecryptException
+ import expo.modules.securestore.SecureStoreModule
+ import expo.modules.securestore.SecureStoreOptions
++import expo.modules.securestore.SecureStoreOriginalFeedback
+ import org.json.JSONException
+ import org.json.JSONObject
+ import java.nio.charset.StandardCharsets
+@@ -86,15 +88,23 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
+     authenticationPrompt: String,
+     authenticationHelper: AuthenticationHelper,
+     enableDeviceFallback: Boolean,
+-  ): JSONObject {
++  ): SecureStoreOriginalFeedback<JSONObject> {
+     val secretKey = keyStoreEntry.secretKey
+     val cipher = Cipher.getInstance(AES_CIPHER)
+     cipher.init(Cipher.ENCRYPT_MODE, secretKey)
+
+     val gcmSpec = cipher.parameters.getParameterSpec(GCMParameterSpec::class.java)
+-    val authenticatedCipher = authenticationHelper.authenticateCipher(cipher, requireAuthentication, authenticationPrompt, enableDeviceFallback)
++    var promptResult: BiometricPrompt.AuthenticationResult? = null
++    val authenticatedCipher: Cipher
++
++    if (requireAuthentication) {
++      promptResult = authenticationHelper.authenticateCipher(cipher, authenticationPrompt, enableDeviceFallback)
++      authenticatedCipher = promptResult.cryptoObject?.cipher ?: cipher
++    } else {
++      authenticatedCipher = cipher
++    }
+
+-    return createEncryptedItemWithCipher(plaintextValue, authenticatedCipher, gcmSpec)
++    return SecureStoreOriginalFeedback(createEncryptedItemWithCipher(plaintextValue, authenticatedCipher, gcmSpec), promptResult)
+   }
+
+   internal fun createEncryptedItemWithCipher(
+@@ -121,7 +131,7 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
+     keyStoreEntry: KeyStore.SecretKeyEntry,
+     options: SecureStoreOptions,
+     authenticationHelper: AuthenticationHelper
+-  ): String {
++  ): SecureStoreOriginalFeedback<String> {
+     val ciphertext = encryptedItem.getString(CIPHERTEXT_PROPERTY)
+     val ivString = encryptedItem.getString(IV_PROPERTY)
+     val authenticationTagLength = encryptedItem.getInt(GCM_AUTHENTICATION_TAG_LENGTH_PROPERTY)
+@@ -135,8 +145,17 @@ class AESEncryptor : KeyBasedEncryptor<KeyStore.SecretKeyEntry> {
+       throw DecryptException("Authentication tag length must be at least $MIN_GCM_AUTHENTICATION_TAG_LENGTH bits long", key, options.keychainService)
+     }
+     cipher.init(Cipher.DECRYPT_MODE, keyStoreEntry.secretKey, gcmSpec)
+-    val unlockedCipher = authenticationHelper.authenticateCipher(cipher, requiresAuthentication, options.authenticationPrompt, options.enableDeviceFallback)
+-    return String(unlockedCipher.doFinal(ciphertextBytes), StandardCharsets.UTF_8)
++    var promptResult: BiometricPrompt.AuthenticationResult? = null
++    val unlockedCipher: Cipher
++
++    if (requiresAuthentication) {
++      promptResult = authenticationHelper.authenticateCipher(cipher, options.authenticationPrompt, options.enableDeviceFallback)
++      unlockedCipher = promptResult.cryptoObject?.cipher ?: cipher
++    } else {
++      unlockedCipher = cipher
++    }
++
++    return SecureStoreOriginalFeedback(String(unlockedCipher.doFinal(ciphertextBytes), StandardCharsets.UTF_8), promptResult)
+   }
+
+   companion object {
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
+index 5f8bbfd..7527001 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/HybridAESEncryptor.kt
+@@ -9,6 +9,7 @@ import expo.modules.securestore.EncryptException
+ import expo.modules.securestore.KeyStoreException
+ import expo.modules.securestore.SecureStoreModule
+ import expo.modules.securestore.SecureStoreOptions
++import expo.modules.securestore.SecureStoreOriginalFeedback
+ import org.json.JSONException
+ import org.json.JSONObject
+ import java.security.GeneralSecurityException
+@@ -71,7 +72,7 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
+     authenticationPrompt: String,
+     authenticationHelper: AuthenticationHelper,
+     enableDeviceFallback: Boolean,
+-  ): JSONObject {
++  ): SecureStoreOriginalFeedback<JSONObject> {
+     // This should never be called after we dropped Android SDK 22 support.
+     throw EncryptException(
+       "HybridAESEncryption should not be used on Android SDK >= 23. This shouldn't happen. " +
+@@ -88,7 +89,7 @@ class HybridAESEncryptor(private var mContext: Context, private val mAESEncrypto
+     keyStoreEntry: KeyStore.PrivateKeyEntry,
+     options: SecureStoreOptions,
+     authenticationHelper: AuthenticationHelper
+-  ): String {
++  ): SecureStoreOriginalFeedback<String> {
+     // Decrypt the encrypted symmetric key
+     val encryptedSecretKeyString = encryptedItem.getString(ENCRYPTED_SECRET_KEY_PROPERTY)
+     val encryptedSecretKeyBytes = Base64.decode(encryptedSecretKeyString, Base64.DEFAULT)
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
+index 39459ff..a531aff 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/encryptors/KeyBasedEncryptor.kt
+@@ -2,6 +2,7 @@ package expo.modules.securestore.encryptors
+
+ import expo.modules.securestore.AuthenticationHelper
+ import expo.modules.securestore.SecureStoreOptions
++import expo.modules.securestore.SecureStoreOriginalFeedback
+ import org.json.JSONException
+ import org.json.JSONObject
+ import java.security.GeneralSecurityException
+@@ -27,7 +28,7 @@ interface KeyBasedEncryptor<E : KeyStore.Entry> {
+     authenticationPrompt: String,
+     authenticationHelper: AuthenticationHelper,
+     enableDeviceFallback: Boolean,
+-  ): JSONObject
++  ): SecureStoreOriginalFeedback<JSONObject>
+
+   @Throws(GeneralSecurityException::class, JSONException::class)
+   suspend fun decryptItem(
+@@ -36,5 +37,5 @@ interface KeyBasedEncryptor<E : KeyStore.Entry> {
+     keyStoreEntry: E,
+     options: SecureStoreOptions,
+     authenticationHelper: AuthenticationHelper
+-  ): String
++  ): SecureStoreOriginalFeedback<String>
+ }
+diff --git a/node_modules/expo-secure-store/build/SecureStore.d.ts b/node_modules/expo-secure-store/build/SecureStore.d.ts
+index 835a4d4..d2da8d9 100644
+--- a/node_modules/expo-secure-store/build/SecureStore.d.ts
++++ b/node_modules/expo-secure-store/build/SecureStore.d.ts
+@@ -1,4 +1,53 @@
++type EmptyObject = Record<string, never>;
++type SecureStoreSetFeedback<R extends SecureStoreOptions> = R['returnUsedAuthenticationType'] extends true ? AuthType : void;
++type SecureStoreGetFeedback<T, R extends SecureStoreOptions> = R['returnUsedAuthenticationType'] extends true ? [T | null, AuthType] : T | null;
+ export type KeychainAccessibilityConstant = number;
++/**
++ * Authentication type returned by the SecureStore after reading item or saving it to the store.
++ */
++export declare const AUTH_TYPE: {
++    /**
++     * This is purely for backwards compatibility.
++     * Although it is not listed as a return value of the getAuthenticationType() method,
++     * it is still present in the Android code.
++     * @see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.AuthenticationResult#getAuthenticationType()
++     * @see https://developer.android.com/reference/androidx/biometric/BiometricPrompt#AUTHENTICATION_RESULT_TYPE_UNKNOWN()
++     * @platform android
++     */
++    readonly UNKNOWN: -1;
++    /**
++     * Returned when the authentication fails
++     * @platform android
++     * @platform ios
++     */
++    readonly NONE: 0;
++    /**
++     * Generic type, not specified whether it was a passcode or pattern.
++     * @platform android
++     * @platform ios
++     */
++    readonly CREDENTIALS: 1;
++    /**
++     * Generic type, not specified whether it was a face scan or a fingerprint
++     * @platform android
++     */
++    readonly BIOMETRICS: 2;
++    /**
++     * FaceID was used to authenticate
++     * @platform ios
++     */
++    readonly FACE_ID: 3;
++    /**
++     * TouchID was used to authenticate
++     * @platform ios
++     */
++    readonly TOUCH_ID: 4;
++    /**
++     * OpticID was used to authenticate (reserved by apple, used on Apple Vision Pro, not iOS)
++     */
++    readonly OPTIC_ID: 5;
++};
++type AuthType = (typeof AUTH_TYPE)[keyof typeof AUTH_TYPE];
+ /**
+  * The data in the keychain item cannot be accessed after a restart until the device has been
+  * unlocked once by the user. This may be useful if you need to access the item when the phone
+@@ -88,6 +137,22 @@ export type SecureStoreOptions = {
+      * @platform ios
+      */
+     enableDeviceFallback?: boolean;
++    /**
++     * When this flag is set to true, the get methods of SecureStore will return a two-element array. The first value will be the original value returned when this flag is set to false.
++     * The second value is the authentication type used to read the value from the AUTH_TYPE object.
++     * As for the set function, the returned value will simply be AUTH_TYPE.
++     *
++     * @warning
++     * If the iOS device supports biometrics and the user falls back to device credentials, it will not be detected.
++     * This is not the case on Android, but we cannot specify the exact type of biometrics (e.g. fingerprint or face scan).
++     * Whether the type is detected correctly depends on the platform and its native implementation.
++     * This should be treated as more of a hint.
++     *
++     * @default false
++     * @platform android
++     * @platform ios
++     */
++    returnUsedAuthenticationType?: boolean;
+ };
+ /**
+  * Returns whether the SecureStore API is enabled on the current device. This does not check the app
+@@ -119,7 +184,7 @@ export declare function deleteItemAsync(key: string, options?: SecureStoreOption
+  * > After a key has been invalidated, it becomes impossible to read its value.
+  * > This only applies to values stored with `requireAuthentication` set to `true`.
+  */
+-export declare function getItemAsync(key: string, options?: SecureStoreOptions): Promise<string | null>;
++export declare function getItemAsync<R extends SecureStoreOptions>(key: string, options?: R | EmptyObject): Promise<SecureStoreGetFeedback<string, R>>;
+ /**
+  * Stores a key–value pair.
+  *
+@@ -129,7 +194,7 @@ export declare function getItemAsync(key: string, options?: SecureStoreOptions):
+  *
+  * @return A promise that rejects if value cannot be stored on the device.
+  */
+-export declare function setItemAsync(key: string, value: string, options?: SecureStoreOptions): Promise<void>;
++export declare function setItemAsync<R extends SecureStoreOptions>(key: string, value: string, options?: R | EmptyObject): Promise<SecureStoreSetFeedback<R>>;
+ /**
+  * Stores a key–value pair synchronously.
+  * > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when the `requireAuthentication` option is set to `true` until the user authenticates.
+@@ -139,7 +204,7 @@ export declare function setItemAsync(key: string, value: string, options?: Secur
+  * @param options An [`SecureStoreOptions`](#securestoreoptions) object.
+  *
+  */
+-export declare function setItem(key: string, value: string, options?: SecureStoreOptions): void;
++export declare function setItem<R extends SecureStoreOptions>(key: string, value: string, options?: R | EmptyObject): SecureStoreSetFeedback<R>;
+ /**
+  * Synchronously reads the stored value associated with the provided key.
+  * > **Note:** This function blocks the JavaScript thread, so the application may not be interactive when reading a value with `requireAuthentication`
+@@ -150,7 +215,7 @@ export declare function setItem(key: string, value: string, options?: SecureStor
+  * @return Previously stored value. It resolves with `null` if there is no entry
+  * for the given key or if the key has been invalidated.
+  */
+-export declare function getItem(key: string, options?: SecureStoreOptions): string | null;
++export declare function getItem<R extends SecureStoreOptions>(key: string, options?: R | EmptyObject): SecureStoreGetFeedback<string, R>;
+ /**
+  * Checks if the value can be saved with `requireAuthentication` option enabled.
+  * @return `true` if the device supports biometric authentication and the enrolled method is sufficiently secure. Otherwise, returns `false`. Always returns false on tvOS.
+diff --git a/node_modules/expo-secure-store/build/SecureStore.js b/node_modules/expo-secure-store/build/SecureStore.js
+index e11bd94..940fc61 100644
+--- a/node_modules/expo-secure-store/build/SecureStore.js
++++ b/node_modules/expo-secure-store/build/SecureStore.js
+@@ -1,6 +1,53 @@
+ import ExpoSecureStore from './ExpoSecureStore';
+ import { byteCountOverLimit, VALUE_BYTES_LIMIT } from './byteCounter';
+ // @needsAudit
++
++/**
++ * Authentication type returned by the SecureStore after reading item or saving it to the store.
++ */
++export const AUTH_TYPE = {
++    /**
++     * This is purely for backwards compatibility.
++     * Although it is not listed as a return value of the getAuthenticationType() method,
++     * it is still present in the Android code.
++     * @see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.AuthenticationResult#getAuthenticationType()
++     * @see https://developer.android.com/reference/androidx/biometric/BiometricPrompt#AUTHENTICATION_RESULT_TYPE_UNKNOWN()
++     * @platform android
++     */
++    UNKNOWN: -1,
++    /**
++     * Returned when the authentication fails
++     * @platform android
++     * @platform ios
++     */
++    NONE: 0,
++    /**
++     * Generic type, not specified whether it was a passcode or pattern.
++     * @platform android
++     * @platform ios
++     */
++    CREDENTIALS: 1,
++    /**
++     * Generic type, not specified whether it was a face scan or a fingerprint
++     * @platform android
++     */
++    BIOMETRICS: 2,
++    /**
++     * FaceID was used to authenticate
++     * @platform ios
++     */
++    FACE_ID: 3,
++    /**
++     * TouchID was used to authenticate
++     * @platform ios
++     */
++    TOUCH_ID: 4,
++    /**
++     * OpticID was used to authenticate (reserved by apple, used on Apple Vision Pro, not iOS)
++     */
++    OPTIC_ID: 5,
++};
++// @needsAudit
+ /**
+  * The data in the keychain item cannot be accessed after a restart until the device has been
+  * unlocked once by the user. This may be useful if you need to access the item when the phone
+@@ -102,7 +149,7 @@ export async function setItemAsync(key, value, options = {}) {
+     if (!isValidValue(value)) {
+         throw new Error(`Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.`);
+     }
+-    await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
++    return await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
+ }
+ /**
+  * Stores a key–value pair synchronously.
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreModule.swift b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+index 1268979..7764c8e 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreModule.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+@@ -18,28 +18,36 @@ public final class SecureStoreModule: Module {
+       "WHEN_UNLOCKED_THIS_DEVICE_ONLY": SecureStoreAccessible.whenUnlockedThisDeviceOnly.rawValue
+     ])
+
+-    AsyncFunction("getValueWithKeyAsync") { (key: String, options: SecureStoreOptions) -> String? in
+-      return try get(with: key, options: options)
++    AsyncFunction("getValueWithKeyAsync") { (key: String, options: SecureStoreOptions) in
++      let result = try get(with: key, options: options)
++
++      return wrapResultWithFeedback(action: .get, result: result, options: options).value
+     }
+
+-    Function("getValueWithKeySync") { (key: String, options: SecureStoreOptions) -> String? in
+-      return try get(with: key, options: options)
++    Function("getValueWithKeySync") { (key: String, options: SecureStoreOptions) in
++      let result = try get(with: key, options: options)
++
++      return wrapResultWithFeedback(action: .get, result: result, options: options).value
+     }
+
+-    AsyncFunction("setValueWithKeyAsync") { (value: String, key: String, options: SecureStoreOptions) -> Bool in
++    AsyncFunction("setValueWithKeyAsync") { (value: String, key: String, options: SecureStoreOptions) in
+       guard let key = validate(for: key) else {
+         throw InvalidKeyException()
+       }
+
+-      return try set(value: value, with: key, options: options)
++      let result = try set(value: value, with: key, options: options)
++
++      return wrapResultWithFeedback(action: .set, result: result, options: options).value
+     }
+
+-    Function("setValueWithKeySync") {(value: String, key: String, options: SecureStoreOptions) -> Bool in
++    Function("setValueWithKeySync") {(value: String, key: String, options: SecureStoreOptions) in
+       guard let key = validate(for: key) else {
+         throw InvalidKeyException()
+       }
+
+-      return try set(value: value, with: key, options: options)
++      let result = try set(value: value, with: key, options: options)
++
++      return wrapResultWithFeedback(action: .set, result: result, options: options).value
+     }
+
+     AsyncFunction("deleteValueWithKeyAsync") { (key: String, options: SecureStoreOptions) in
+@@ -53,18 +61,7 @@ public final class SecureStoreModule: Module {
+     }
+
+     Function("canUseBiometricAuthentication") {() -> Bool in
+-      #if os(tvOS)
+-      return false
+-      #else
+-      let context = LAContext()
+-      var error: NSError?
+-      let isBiometricsSupported: Bool = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error)
+-
+-      if error != nil {
+-        return false
+-      }
+-      return isBiometricsSupported
+-      #endif
++      return areBiometricsEnabled()
+     }
+
+     Function("canUseDeviceCredentialsAuthentication") { () -> Bool in
+@@ -76,6 +73,41 @@ public final class SecureStoreModule: Module {
+     return LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: nil)
+   }
+
++  private func getAuthType() -> AuthType {
++    if !areBiometricsEnabled() {return AuthType.credentials}
++    let biometryType = LAContext().biometryType
++
++    switch biometryType {
++      case .faceID: return .faceID
++      case .touchID: return .touchID
++      case .opticID: return .opticID // available since iOS 17
++      case .none: fallthrough // this one continues to the next line
++      @unknown default: return .credentials
++    }
++  }
++
++  private func wrapResultWithFeedback<T>(action: SecureStoreFeedbackAction, result: T, options: SecureStoreOptions) -> any SecureStoreFeedback {
++    let authType = getAuthType().rawValue
++
++    if (!options.returnUsedAuthenticationType) {
++      return SecureStoreOriginalFeedback(source: result, authType: authType)
++    }
++
++    if (action == .get) {
++      return SecureStoreGetFeedback(source: result, authType: authType)
++    }
++
++    return SecureStoreSetFeedback(source: result, authType: authType)
++  }
++
++  private func areBiometricsEnabled() -> Bool {
++    #if os(tvOS)
++      return false
++    #else
++      return LAContext().canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: nil)
++    #endif
++  }
++
+   private func get(with key: String, options: SecureStoreOptions) throws -> String? {
+     guard let key = validate(for: key) else {
+       throw InvalidKeyException()
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+index c9e843b..b95eca7 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+@@ -18,4 +18,71 @@ internal struct SecureStoreOptions: Record {
+
+   @Field
+   var enableDeviceFallback: Bool = false
++
++  @Field
++  var returnUsedAuthenticationType: Bool = false
++}
++
++@available(iOS 11.2, *)
++public enum AuthType: Int, @unchecked Sendable {
++  /// The device does not support biometry.
++  case none = 0
++
++  /// The device supports device credentials
++  case credentials = 1
++
++  /// Generic type, not specified whether it was a faceID or touchID
++  case biometrics = 2
++
++  /// The device supports Face ID.
++  case faceID = 3
++
++  /// The device supports Touch ID.
++  case touchID = 4
++
++  /// The device supports Optic ID
++  case opticID = 5
++}
++
++public enum SecureStoreFeedbackAction: String {
++  case set
++  case get
++}
++
++protocol SecureStoreFeedback {
++  associatedtype Source
++  associatedtype Value
++  var source: Source { get }
++  var authType: Int { get }
++  var value: Value { get }
++}
++
++struct SecureStoreGetFeedback<T>: SecureStoreFeedback {
++  typealias Source = T
++  typealias Value = Array<Any>
++  var source: Source
++  var authType: Int = AuthType.none.rawValue
++  var value: Value {
++    get { return [source, authType] }
++  }
++}
++
++struct SecureStoreOriginalFeedback<T>: SecureStoreFeedback {
++  typealias Source = T
++  typealias Value = T
++  var source: Source
++  var authType: Int = AuthType.none.rawValue
++  var value: Value {
++    get { return source }
++  }
++}
++
++struct SecureStoreSetFeedback<T>: SecureStoreFeedback {
++  typealias Source = T
++  typealias Value = Int
++  var source: Source
++  var authType: Int = AuthType.none.rawValue
++  var value: Value {
++    get { return authType }
++  }
+ }
+diff --git a/node_modules/expo-secure-store/src/SecureStore.ts b/node_modules/expo-secure-store/src/SecureStore.ts
+index 4f253a1..d44c0bb 100644
+--- a/node_modules/expo-secure-store/src/SecureStore.ts
++++ b/node_modules/expo-secure-store/src/SecureStore.ts
+@@ -1,8 +1,63 @@
+ import ExpoSecureStore from './ExpoSecureStore';
+ import { byteCountOverLimit, VALUE_BYTES_LIMIT } from './byteCounter';
+
++type EmptyObject = Record<string, never>;
++type SecureStoreSetFeedback<R extends SecureStoreOptions> =
++    R['returnUsedAuthenticationType'] extends true ? AuthType : void;
++type SecureStoreGetFeedback<
++    T,
++    R extends SecureStoreOptions,
++> = R['returnUsedAuthenticationType'] extends true ? [T | null, AuthType] : T | null;
+ export type KeychainAccessibilityConstant = number;
+
++/**
++ * Authentication type returned by the SecureStore after reading item or saving it to the store.
++ */
++export const AUTH_TYPE = {
++    /**
++     * This is purely for backwards compatibility.
++     * Although it is not listed as a return value of the getAuthenticationType() method,
++     * it is still present in the Android code.
++     * @see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.AuthenticationResult#getAuthenticationType()
++     * @see https://developer.android.com/reference/androidx/biometric/BiometricPrompt#AUTHENTICATION_RESULT_TYPE_UNKNOWN()
++     * @platform android
++     */
++    UNKNOWN: -1,
++    /**
++     * Returned when the authentication fails
++     * @platform android
++     * @platform ios
++     */
++    NONE: 0,
++    /**
++     * Generic type, not specified whether it was a passcode or pattern.
++     * @platform android
++     * @platform ios
++     */
++    CREDENTIALS: 1,
++    /**
++     * Generic type, not specified whether it was a face scan or a fingerprint
++     * @platform android
++     */
++    BIOMETRICS: 2,
++    /**
++     * FaceID was used to authenticate
++     * @platform ios
++     */
++    FACE_ID: 3,
++    /**
++     * TouchID was used to authenticate
++     * @platform ios
++     */
++    TOUCH_ID: 4,
++    /**
++     * OpticID was used to authenticate (reserved by apple, used on Apple Vision Pro, not iOS)
++     */
++    OPTIC_ID: 5,
++} as const;
++
++type AuthType = (typeof AUTH_TYPE)[keyof typeof AUTH_TYPE];
++
+ // @needsAudit
+ /**
+  * The data in the keychain item cannot be accessed after a restart until the device has been
+@@ -113,6 +168,23 @@ export type SecureStoreOptions = {
+    * @platform ios
+    */
+   enableDeviceFallback?: boolean;
++
++  /**
++   * When this flag is set to true, the get methods of SecureStore will return a two-element array. The first value will be the original value returned when this flag is set to false.
++   * The second value is the authentication type used to read the value from the AUTH_TYPE object.
++   * As for the set function, the returned value will simply be AUTH_TYPE.
++   *
++   * @warning
++   * If the iOS device supports biometrics and the user falls back to device credentials, it will not be detected.
++   * This is not the case on Android, but we cannot specify the exact type of biometrics (e.g. fingerprint or face scan).
++   * Whether the type is detected correctly depends on the platform and its native implementation.
++   * This should be treated as more of a hint.
++   *
++   * @default false
++   * @platform android
++   * @platform ios
++   */
++  returnUsedAuthenticationType?: boolean;
+ };
+
+ // @needsAudit
+@@ -159,10 +231,10 @@ export async function deleteItemAsync(
+  * > After a key has been invalidated, it becomes impossible to read its value.
+  * > This only applies to values stored with `requireAuthentication` set to `true`.
+  */
+-export async function getItemAsync(
++export async function getItemAsync<R extends SecureStoreOptions>(
+   key: string,
+-  options: SecureStoreOptions = {}
+-): Promise<string | null> {
++  options: R | EmptyObject = {}
++): Promise<SecureStoreGetFeedback<string, R>> {
+   ensureValidKey(key);
+   return await ExpoSecureStore.getValueWithKeyAsync(key, options);
+ }
+@@ -177,11 +249,11 @@ export async function getItemAsync(
+  *
+  * @return A promise that rejects if value cannot be stored on the device.
+  */
+-export async function setItemAsync(
++export async function setItemAsync<R extends SecureStoreOptions>(
+   key: string,
+   value: string,
+-  options: SecureStoreOptions = {}
+-): Promise<void> {
++  options: R | EmptyObject = {}
++): Promise<SecureStoreSetFeedback<R>> {
+   ensureValidKey(key);
+   if (!isValidValue(value)) {
+     throw new Error(
+@@ -189,7 +261,7 @@ export async function setItemAsync(
+     );
+   }
+
+-  await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
++  return await ExpoSecureStore.setValueWithKeyAsync(value, key, options);
+ }
+
+ /**
+@@ -201,7 +273,11 @@ export async function setItemAsync(
+  * @param options An [`SecureStoreOptions`](#securestoreoptions) object.
+  *
+  */
+-export function setItem(key: string, value: string, options: SecureStoreOptions = {}): void {
++export function setItem<R extends SecureStoreOptions>(
++  key: string,
++  value: string,
++  options: R | EmptyObject = {}
++): SecureStoreSetFeedback<R> {
+   ensureValidKey(key);
+   if (!isValidValue(value)) {
+     throw new Error(
+@@ -222,7 +298,10 @@ export function setItem(key: string, value: string, options: SecureStoreOptions
+  * @return Previously stored value. It resolves with `null` if there is no entry
+  * for the given key or if the key has been invalidated.
+  */
+-export function getItem(key: string, options: SecureStoreOptions = {}): string | null {
++export function getItem<R extends SecureStoreOptions>(
++  key: string,
++  options: R | EmptyObject = {}
++): SecureStoreGetFeedback<string, R> {
+   ensureValidKey(key);
+   return ExpoSecureStore.getValueWithKeySync(key, options);
+ }
diff --git a/patches/expo-secure-store/expo-secure-store+14.2.4+003+force-authentication-on-save.patch b/patches/expo-secure-store/expo-secure-store+14.2.4+003+force-authentication-on-save.patch
new file mode 100644
index 000000000000..59e74054b162
--- /dev/null
+++ b/patches/expo-secure-store/expo-secure-store+14.2.4+003+force-authentication-on-save.patch
@@ -0,0 +1,124 @@
+diff --git a/node_modules/expo-secure-store/build/SecureStore.d.ts b/node_modules/expo-secure-store/build/SecureStore.d.ts
+index 7097a8a..975d75b 100644
+--- a/node_modules/expo-secure-store/build/SecureStore.d.ts
++++ b/node_modules/expo-secure-store/build/SecureStore.d.ts
+@@ -153,6 +153,20 @@ export type SecureStoreOptions = {
+      * @platform ios
+      */
+     returnUsedAuthenticationType?: boolean;
++    /**
++     * On iOS, the system does not ask for auth when saving the value to the SecureStore.
++     * The Android however, displays the authentication prompt when saving the value.
++     * To keep the behavior on every platform similar as much as possible,
++     * setting this flag to true will ensure that authentication is required when saving a value to the store.
++     *
++     * @warning: This flag only works for the asynchronous version of the SecureStore save method.
++     * It should only be considered an improvement to the user experience; it does not prevent the user
++     * from saving the value without authenticating using other methods (e.g. by directly modifying the keychain).
++     * For it to take effect, the 'requireAuthentication' flag must be set to true.
++     * @default false
++     * @platform ios
++     */
++    forceAuthenticationOnSave?: boolean;
+ };
+ /**
+  * Returns whether the SecureStore API is enabled on the current device. This does not check the app
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreModule.swift b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+index 7764c8e..02f837c 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreModule.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+@@ -35,6 +35,10 @@ public final class SecureStoreModule: Module {
+         throw InvalidKeyException()
+       }
+ 
++      if options.requireAuthentication && options.forceAuthenticationOnSave {
++        try await triggerPolicy(options: options)
++      }
++
+       let result = try set(value: value, with: key, options: options)
+ 
+       return wrapResultWithFeedback(action: .set, result: result, options: options).value
+@@ -190,6 +194,32 @@ public final class SecureStoreModule: Module {
+     }
+   }
+ 
++  @MainActor
++  private func triggerPolicy(options: SecureStoreOptions) async throws {
++    let isPolicyAvailable = options.enableDeviceFallback ? areDeviceCredentialsEnabled() : areBiometricsEnabled()
++
++    guard isPolicyAvailable else {
++      throw SecureStoreRuntimeError("No authentication method available")
++    }
++
++    let localAuthPolicy: LAPolicy = options.enableDeviceFallback ? .deviceOwnerAuthentication : .deviceOwnerAuthenticationWithBiometrics
++    let localizedReason: String = options.authenticationPrompt ?? "Authentication required"
++
++    let success: Bool = try await withCheckedThrowingContinuation { continuation in
++      LAContext().evaluatePolicy(localAuthPolicy, localizedReason: localizedReason) { success, error in
++        if let error = error {
++          continuation.resume(throwing: error)
++        } else {
++          continuation.resume(returning: success)
++        }
++      }
++    }
++
++    guard success else {
++      throw SecureStoreRuntimeError("Unable to authenticate")
++    }
++  }
++
+   private func searchKeyChain(with key: String, options: SecureStoreOptions, requireAuthentication: Bool? = nil) throws -> Data? {
+     var query = query(with: key, options: options, requireAuthentication: requireAuthentication)
+ 
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+index b95eca7..321c7e5 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+@@ -21,6 +21,9 @@ internal struct SecureStoreOptions: Record {
+ 
+   @Field
+   var returnUsedAuthenticationType: Bool = false
++
++  @Field
++  var forceAuthenticationOnSave: Bool = false
+ }
+ 
+ @available(iOS 11.2, *)
+@@ -86,3 +89,10 @@ struct SecureStoreSetFeedback<T>: SecureStoreFeedback {
+     get { return authType }
+   }
+ }
++
++struct SecureStoreRuntimeError: LocalizedError {
++  let errorDescription: String?
++  init(_ description: String) {
++    self.errorDescription = description
++  }
++}
+diff --git a/node_modules/expo-secure-store/src/SecureStore.ts b/node_modules/expo-secure-store/src/SecureStore.ts
+index d44c0bb..c4b95ae 100644
+--- a/node_modules/expo-secure-store/src/SecureStore.ts
++++ b/node_modules/expo-secure-store/src/SecureStore.ts
+@@ -185,6 +185,21 @@ export type SecureStoreOptions = {
+    * @platform ios
+    */
+   returnUsedAuthenticationType?: boolean;
++
++  /**
++   * On iOS, the system does not ask for auth when saving the value to the SecureStore.
++   * The Android however, displays the authentication prompt when saving the value.
++   * To keep the behavior on every platform similar as much as possible,
++   * setting this flag to true will ensure that authentication is required when saving a value to the store.
++   *
++   * @warning: This flag only works for the asynchronous version of the SecureStore save method.
++   * It should only be considered an improvement to the user experience; it does not prevent the user
++   * from saving the value without authenticating using other methods (e.g. by directly modifying the keychain).
++   * For it to take effect, the 'requireAuthentication' flag must be set to true.
++   * @default false
++   * @platform ios
++   */
++  forceAuthenticationOnSave?: boolean;
+ };
+ 
+ // @needsAudit
diff --git a/patches/expo-secure-store/expo-secure-store+14.2.4+004+fail-on-update.patch b/patches/expo-secure-store/expo-secure-store+14.2.4+004+fail-on-update.patch
new file mode 100644
index 000000000000..c3acba54c616
--- /dev/null
+++ b/patches/expo-secure-store/expo-secure-store+14.2.4+004+fail-on-update.patch
@@ -0,0 +1,100 @@
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
+index d209efc..e362aef 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreModule.kt
+@@ -212,6 +212,10 @@ open class SecureStoreModule : Module() {
+       return SecureStoreOriginalFeedback(null)
+     }
+ 
++    if (prefs.contains(keychainAwareKey) && options.failOnUpdate) {
++      throw WriteException("Key already exists", key, options.keychainService)
++    }
++
+     try {
+       if (keyIsInvalidated) {
+         // Invalidated keys will block writing even though it's not possible to re-validate them
+diff --git a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
+index 4e30440..16674b8 100644
+--- a/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
++++ b/node_modules/expo-secure-store/android/src/main/java/expo/modules/securestore/SecureStoreOptions.kt
+@@ -11,7 +11,8 @@ class SecureStoreOptions(
+   @Field var keychainService: String = SecureStoreModule.DEFAULT_KEYSTORE_ALIAS,
+   @Field var requireAuthentication: Boolean = false,
+   @Field var enableDeviceFallback: Boolean = false,
+-  @Field var returnUsedAuthenticationType: Boolean = false
++  @Field var returnUsedAuthenticationType: Boolean = false,
++  @Field var failOnUpdate: Boolean = false
+ ) : Record, Serializable
+ 
+ enum class SecureStoreAuthType(index: Int) {
+diff --git a/node_modules/expo-secure-store/build/SecureStore.d.ts b/node_modules/expo-secure-store/build/SecureStore.d.ts
+index f1e3610..8954c59 100644
+--- a/node_modules/expo-secure-store/build/SecureStore.d.ts
++++ b/node_modules/expo-secure-store/build/SecureStore.d.ts
+@@ -167,6 +167,16 @@ export type SecureStoreOptions = {
+      * @platform ios
+      */
+     forceAuthenticationOnSave?: boolean;
++    /**
++     * If the key has already been stored, the save function will throw an error instead of overwriting it.
++     * The behaviour differs slightly depending on the platform.
++     * On Android, an error is thrown before the authentication prompt. On iOS, an error is thrown after authentication.
++     *
++     * @default false
++     * @platform ios
++     * @platform android
++     */
++    failOnUpdate?: boolean;
+ };
+ /**
+  * Returns whether the SecureStore API is enabled on the current device. This does not check the app
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreModule.swift b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+index 02f837c..47282db 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreModule.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+@@ -169,6 +169,9 @@ public final class SecureStoreModule: Module {
+       SecItemDelete(query(with: key, options: options, requireAuthentication: !options.requireAuthentication) as CFDictionary)
+       return true
+     case errSecDuplicateItem:
++      if options.failOnUpdate {
++        throw SecureStoreRuntimeError("Key already exists")
++      }
+       return try update(value: value, with: key, options: options)
+     default:
+       throw KeyChainException(status)
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+index 321c7e5..6a787b0 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+@@ -24,6 +24,9 @@ internal struct SecureStoreOptions: Record {
+ 
+   @Field
+   var forceAuthenticationOnSave: Bool = false
++
++  @Field
++  var failOnUpdate: Bool = false
+ }
+ 
+ @available(iOS 11.2, *)
+diff --git a/node_modules/expo-secure-store/src/SecureStore.ts b/node_modules/expo-secure-store/src/SecureStore.ts
+index c4b95ae..0bedcaa 100644
+--- a/node_modules/expo-secure-store/src/SecureStore.ts
++++ b/node_modules/expo-secure-store/src/SecureStore.ts
+@@ -200,6 +200,17 @@ export type SecureStoreOptions = {
+    * @platform ios
+    */
+   forceAuthenticationOnSave?: boolean;
++
++  /**
++   * If the key has already been stored, the save function will throw an error instead of overwriting it.
++   * The behaviour differs slightly depending on the platform.
++   * On Android, an error is thrown before the authentication prompt. On iOS, an error is thrown after authentication.
++   *
++   * @default false
++   * @platform ios
++   * @platform android
++   */
++  failOnUpdate?: boolean;
+ };
+ 
+ // @needsAudit
diff --git a/patches/expo-secure-store/expo-secure-store+14.2.4+005+force-read-authentication-on-simulators.patch b/patches/expo-secure-store/expo-secure-store+14.2.4+005+force-read-authentication-on-simulators.patch
new file mode 100644
index 000000000000..f65e0a31540a
--- /dev/null
+++ b/patches/expo-secure-store/expo-secure-store+14.2.4+005+force-read-authentication-on-simulators.patch
@@ -0,0 +1,77 @@
+diff --git a/node_modules/expo-secure-store/build/SecureStore.d.ts b/node_modules/expo-secure-store/build/SecureStore.d.ts
+index 836958c..29531c5 100644
+--- a/node_modules/expo-secure-store/build/SecureStore.d.ts
++++ b/node_modules/expo-secure-store/build/SecureStore.d.ts
+@@ -177,6 +177,18 @@ export type SecureStoreOptions = {
+      * @platform android
+      */
+     failOnUpdate?: boolean;
++    /**
++     * The LocalAuthentication behaves slightly differently on iOS simulators.
++     * In numerous cases, the authentication prompts are skipped on simulators (as opposed to real devices).
++     * Setting this flag to true forces the prompt to appear on simulators when a value with the `requireAuthentication` flag set to true is read.
++     * This is purely for testing the app on simulators, in cases where the prompt does not appear when the value is read.
++     * This has no effect on real devices.
++     *
++     * @warning: This flag only works for the asynchronous version of the SecureStore read method.
++     * @default false
++     * @platform ios
++     */
++    forceReadAuthenticationOnSimulators?: boolean;
+ };
+ /**
+  * Returns whether the SecureStore API is enabled on the current device. This does not check the app
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreModule.swift b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+index 47282db..dcbe242 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreModule.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreModule.swift
+@@ -19,6 +19,11 @@ public final class SecureStoreModule: Module {
+     ])
+ 
+     AsyncFunction("getValueWithKeyAsync") { (key: String, options: SecureStoreOptions) in
++      #if targetEnvironment(simulator)
++        if options.requireAuthentication && options.forceReadAuthenticationOnSimulators {
++          try await triggerPolicy(options: options)
++        }
++      #endif
+       let result = try get(with: key, options: options)
+ 
+       return wrapResultWithFeedback(action: .get, result: result, options: options).value
+diff --git a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+index 6a787b0..8f110dd 100644
+--- a/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
++++ b/node_modules/expo-secure-store/ios/SecureStoreOptions.swift
+@@ -27,6 +27,9 @@ internal struct SecureStoreOptions: Record {
+ 
+   @Field
+   var failOnUpdate: Bool = false
++
++  @Field
++  var forceReadAuthenticationOnSimulators: Bool = false
+ }
+ 
+ @available(iOS 11.2, *)
+diff --git a/node_modules/expo-secure-store/src/SecureStore.ts b/node_modules/expo-secure-store/src/SecureStore.ts
+index 0bedcaa..bae5580 100644
+--- a/node_modules/expo-secure-store/src/SecureStore.ts
++++ b/node_modules/expo-secure-store/src/SecureStore.ts
+@@ -211,6 +211,19 @@ export type SecureStoreOptions = {
+    * @platform android
+    */
+   failOnUpdate?: boolean;
++
++  /**
++   * The LocalAuthentication behaves slightly differently on iOS simulators.
++   * In numerous cases, the authentication prompts are skipped on simulators (as opposed to real devices).
++   * Setting this flag to true forces the prompt to appear on simulators when a value with the `requireAuthentication` flag set to true is read.
++   * This is purely for testing the app on simulators, in cases where the prompt does not appear when the value is read.
++   * This has no effect on real devices.
++   *
++   * @warning: This flag only works for the asynchronous version of the SecureStore read method.
++   * @default false
++   * @platform ios
++   */
++  forceReadAuthenticationOnSimulators?: boolean;
+ };
+ 
+ // @needsAudit
