From 0f259fb8af690ea96f7e04ec702264c88815c501 Mon Sep 17 00:00:00 2001 From: Eduardo Speroni Date: Tue, 4 Aug 2026 12:45:25 -0300 Subject: [PATCH] fix(secure-storage): make the ios simulator NSUserDefaults fallback opt-in * `disableFallbackToUserDefaults` now defaults to `true`, so the keychain is used everywhere including the simulator. Xcode embeds entitlements into simulator builds since Xcode 9, so the `-34018` (errSecMissingEntitlement) failure the fallback worked around no longer occurs. * The fallback wrote raw, unprefixed keys into the same `NSUserDefaults` domain that `ApplicationSettings` uses, so "secure" values collided with plain application settings and could clobber them while migrating plaintext values into secure storage. * Simulator detection no longer consults `UIDevice.currentDevice.name`. That name is user-editable, so naming a real device "my simulator" silently downgraded every read and write to plaintext `NSUserDefaults`. Detection is now solely the `SIMULATOR_DEVICE_NAME` environment variable, which only the simulator runtime sets. The accompanying iOS 9 version gate was dead code given the plugin's deployment target. BREAKING CHANGES: * Values stored on the iOS simulator through the old `NSUserDefaults` fallback are not visible to the keychain path and will read back as `null`. Pass `disableFallbackToUserDefaults: false` as the second constructor argument to restore the previous behaviour. --- packages/secure-storage/README.md | 12 +++++++++--- packages/secure-storage/index.ios.ts | 16 ++++------------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/secure-storage/README.md b/packages/secure-storage/README.md index 4e23a1ec..31762d8e 100644 --- a/packages/secure-storage/README.md +++ b/packages/secure-storage/README.md @@ -113,9 +113,15 @@ const secureStorage = new SecureStorage(kSecAttrAccessibleWhenUnlockedThisDevice ## iOS Simulator -Currently this plugin defaults to using `NSUserDefaults` on **iOS Simulators**. You can change this behaviour by providing `disableFallbackToUserDefaults` to the constructor of `SecureStorage`. This then uses the keychain instead of `NSUserDefaults` on simulators. +The plugin uses the keychain on **iOS Simulators** just like it does on device, so no configuration is needed. -If you're running into issues similar to [issue_10](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/10), consider using the default behaviour again. +An opt-in fallback to `NSUserDefaults` on the simulator is still available for toolchains old enough that `SecItemAdd` fails there with `-34018` (`errSecMissingEntitlement`), see [issue_10](https://github.com/EddyVerbruggen/nativescript-secure-storage/issues/10). Enable it by passing `disableFallbackToUserDefaults: false` as the second constructor argument: + +```ts +const secureStorage = new SecureStorage(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, false); +``` + +Note that the fallback stores values **unencrypted**, under the raw key, in the same `NSUserDefaults` domain that `ApplicationSettings` writes to, so those keys can collide with plain application settings. Only turn it on if the simulator keychain is genuinely unavailable to you. ## iOS Keychain Access/App Groups @@ -152,7 +158,7 @@ To setup: ``` ## Credits -* On __iOS__ we're leveraging the KeyChain using the [SAMKeychain](https://github.com/soffes/SAMKeychain) library (on the Simulator `NSUserDefaults`), +* On __iOS__ we're leveraging the KeyChain using the [SAMKeychain](https://github.com/soffes/SAMKeychain) library, * On __Android__ we're using [Hawk](https://github.com/orhanobut/hawk) library which internally uses [Facebook conceal](https://github.com/facebook/conceal). * Thanks, [Prabu Devarrajan](https://github.com/prabudevarrajan) for [adding the `deleteAll` function](https://github.com/EddyVerbruggen/nativescript-secure-storage/pull/11)! * Thank you [Eddy Verbruggen](https://github.com/EddyVerbruggen) for all the years of service and great work! diff --git a/packages/secure-storage/index.ios.ts b/packages/secure-storage/index.ios.ts index bc6f8610..91162123 100644 --- a/packages/secure-storage/index.ios.ts +++ b/packages/secure-storage/index.ios.ts @@ -11,23 +11,15 @@ export class SecureStorage extends SecureStorageCommon { // This is a copy of 'kSSKeychainAccountKey_copy' which is not exposed from SSKeychain.h by {N} private static kSSKeychainAccountKey_copy: string = 'acct'; - constructor(accessibilityType: string = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, disableFallbackToUserDefaults = false) { + constructor(accessibilityType: string = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, disableFallbackToUserDefaults = true) { super(); if (disableFallbackToUserDefaults) { this.isSimulator = false; } else { - const isMinIOS9 = NSProcessInfo.processInfo.isOperatingSystemAtLeastVersion({ - majorVersion: 9, - minorVersion: 0, - patchVersion: 0, - }); - if (isMinIOS9) { - const simDeviceName = NSProcessInfo.processInfo.environment.objectForKey('SIMULATOR_DEVICE_NAME'); - this.isSimulator = simDeviceName !== null; - } else { - this.isSimulator = UIDevice.currentDevice.name.toLowerCase().indexOf('simulator') > -1; - } + // Only the simulator runtime sets this; anything derived from the device name can be spoofed by renaming a real device. + const simDeviceName = NSProcessInfo.processInfo.environment.objectForKey('SIMULATOR_DEVICE_NAME'); + this.isSimulator = simDeviceName !== null; } this.accessibilityType = accessibilityType;