Delete sensitive info when app is reinstalled
gavin-gmlab opened this issue · 7 comments
I currently use Sensitive Info to save the login tokens etc. I now have a problem where if a user uninstalls the app without logging out, then reinstalls the app on the same phone, the app crashes because it still thinks its logged in.
How can I make sure to delete any relevant information in keychain upon 1st Install? Can this be included in the base library?
I found a workaround on StackOverflow which suggested putting this native code in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Basically
//Clear keychain on first run in case of reinstallation
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"FirstRun"]) {
// Delete values from keychain here
[[NSUserDefaults standardUserDefaults] setValue:@"1strun" forKey:@"FirstRun"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
I need to insert code in the //Delete values from keychain section, but am not sure how to go about doing this.
You may find the response from eskimo useful here: https://forums.developer.apple.com/thread/36442
For reference, the Stackoverflow thread @gavin-gmlab mentioned in his original post is https://stackoverflow.com/questions/4747404/delete-keychain-items-when-an-app-is-uninstalled
In my case, I'm using react-native-sensitive-info through redux-persist-sensitive-storage (through redux-persist). That means I only need to delete one key. My solution looks like this:
// Clear keychain on first run in case of reinstallation
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"FirstRun"]) {
// The default value is "app"
NSString * keychainService = @"THE_KEYCHAIN_SERVICE_VALUE_USED";
// If you're using redux-persist, the key value is whatever you used as your key setting
// prefixed with "persist:"
NSString * key = @"THE_KEY_YOU_WISH_TO_DELETE";
NSLog(@"First run. Attempting to purge any existing persisted state.");
// This may need to be different depending on what
// react-native-sensitve-info options you're using
NSDictionary* query = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword, kSecClass,
keychainService, kSecAttrService,
key, kSecAttrAccount,
kCFBooleanTrue, kSecReturnAttributes,
kCFBooleanTrue, kSecReturnData, nil];
OSStatus osStatus = SecItemDelete((CFDictionaryRef) query);
if (osStatus == noErr) {
NSLog(@"Persisted state purged.");
}
if (osStatus != noErr && osStatus != errSecItemNotFound) {
NSLog(
@"An unknown error occured when clearing persisted state. Ignoring. %d",
osStatus);
}
if (osStatus == errSecItemNotFound) {
NSLog(@"No previously persisted state was found.");
}
[[NSUserDefaults standardUserDefaults] setValue:@"1strun" forKey:@"FirstRun"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
This is based on the implementation at
react-native-sensitive-info/ios/RNSensitiveInfo/RNSensitiveInfo.m
Lines 237 to 260 in 808e81f
In the original Stackoverflow thread, someone pointed at a potential problem with using NSUserDefaults for this purpose: https://stackoverflow.com/questions/20269116/nsuserdefaults-losing-its-keys-values-when-phone-is-rebooted-but-not-unlocked It's unclear to me whether or not the issue discussed in that thread is ongoing or if it was resolved by Apple. If it is still a problem, your keys might be deleted in certain conditions when you wouldn't expect them to.
In the original Stackoverflow thread, someone pointed at a potential problem with using NSUserDefaults for this purpose: https://stackoverflow.com/questions/20269116/nsuserdefaults-losing-its-keys-values-when-phone-is-rebooted-but-not-unlocked It's unclear to me whether or not the issue discussed in that thread is ongoing or if it was resolved by Apple. If it is still a problem, your keys might be deleted in certain conditions when you wouldn't expect them to.
I ended up running into what I assume is this exact issue but I haven't been able to work out a solution just yet.
You can do this without needing to modify native code by implementing AsyncStorage as that stored value will be gone between uninstalls
import AsyncStorage from '@react-native-community/async-storage';
...
try {
const firstRun = await AsyncStorage.getItem('firstRun');
if (!firstRun) {
await SInfo.deleteItem(key, secureStorageConfig);
await AsyncStorage.setItem('firstRun', 'true');
} else {
const persistedState = await SInfo.getItem(
key,
secureStorageConfig,
);
if (persistedState) {
// do something with existing values
}
}
} catch (error) {
console.log({ error });
}
...
@virdesai That seems really promising. Thanks. I will give it a try.
Do you happen to know offhand if there are async-storage docs which explicitly say that the the values are not persisted between installs?
Just as an update to the following comment I made back in the day:
I ended up running into what I assume is this exact issue but I haven't been able to work out a solution just yet.
I actually don't think this is the issue I was running into and that the solution I proposed was working as designed. It's really hard to say at this point though as many other things have changed with the implementation I was working with.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.