Delete keychain items when an app is uninstalled
Asked Answered
S

9

270

I am using idandersen's scifihifi-iphone code for keychain and save password using

[SFHFKeychainUtils storeUsername:@"User" andPassword:@"123"
              forServiceName:@"TestService" updateExisting:YES error:&error];

When I delete the application from the device, the password remains in the keychain.

I want to remove the password from the keychain when the user deletes the application from the device. How can I do this?

Snowonthemountain answered 20/1, 2011 at 12:59 Comment(2)
Since your code doesn't run when your application is being deleted, you have no way of doing this.Brumley
I think that you can delete a keychain item only from inside the app, but not before uninstall it. You can take a look at deleteItem method of SFHFKeychainUtils to delete an username or a password from the keychain.Arthurarthurian
C
436

You can take advantage of the fact that NSUserDefaults are cleared by uninstallation of an app. For example:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //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];
    }

    //...Other stuff that usually happens in didFinishLaunching
}

This checks for and sets a "FirstRun" key/value in NSUserDefaults on the first run of your app if it's not already set. There's a comment where you should put code to delete values from the keychain. Synchronize can be called to make sure the "FirstRun" key/value is immediately persisted in case the user kills the app manually before the system persists it.

Certie answered 1/6, 2011 at 15:0 Comment(9)
I agree with Amro that you can delete/purge your keychain upon the first run of the application. This will clear out everything that was set prior to the app being uninstalled the last time. I did this for one of my apps that stores Facebook/Twitter credentials and it's been working pretty well knowing the fact that only your app has access to whatever keychain that was set.Socha
NSUserDefaults are not cleared when the user manually quits the app. Only values that you've set but either the system (periodically) or you have not yet synchronized with the disk (by calling synchronize) are lost in that case. It's a good idea to call synchronize after setting the first run key. And yes, NSUserDefaults are cleared when a device is reset (and not restored from backup), and that's fine in this case.Certie
You're wrong and you're probably doing something that's causing user defaults to be cleared. The entire point of NSUserDefaults is to save preferences and have those preferences persist through multiple application launches. Again, resetting the device or deleting an app will remove user defaults. Look at how many people have up-voted this answer and check your code. Then go read the documentation. Heck, send me the relevant code and I'll show you what you're doing wrong. It's been this way since iOS 2.0. Down vote away but I'd suggest writing an isolated, simple, test case first.Certie
P.S. And here's an article that explains it in detail chrisrisner.com/…Certie
Oh...you are right. Sorry. Hard to find the test code in 800 files. I'll update my answer.Shikoku
I would not be very confident on using NSUserDefault for this. Why? Have a look at that thread: #20269616. If you start your app from the background, there are cases where your custom keys in NSUserDefaults are just not set. Applying this answer would lead to delete your Keychain custom keys although you really didn't want that!Mythomania
@AugustinePA they're not suggesting saving user credentials into UserDefaults. They're suggesting to store a flag that specifies whether the app has previously been run. That way if the UserDefaults (which clears on app uninstall) states the app has not been previously run, but the keychain contains items, you can delete the items.Pantomime
Can the user remove these flags in any way? (For example by deleting app cached data etc.). If yes then this would also delete keychain values which looks like an undesirable side effect.Muro
@Muro If they delete userdefault flags, then its their own fault. Can't guard against data tampering. Unless you encrypt the userdefault API.Intermixture
O
54

For users looking for a Swift 3.0 version of @amro's answer:

let userDefaults = UserDefaults.standard

if !userDefaults.bool(forKey: "hasRunBefore") {
     // Remove Keychain items here

     // Update the flag indicator
     userDefaults.set(true, forKey: "hasRunBefore")
}

*note that synchronize() function is deprecated

Oxbridge answered 22/11, 2016 at 2:4 Comment(2)
if !userDefaults.bool(forKey: "hasRunBefore") { It is just cleaner.Disaccharide
The synchronize call should be removed.Gerik
M
34

There is no trigger to perform code when the app is deleted from the device. Access to the keychain is dependant on the provisioning profile that is used to sign the application. Therefore no other applications would be able to access this information in the keychain.

It does not help with you aim to remove the password in the keychain when the user deletes application from the device but it should give you some comfort that the password is not accessible (only from a re-install of the original application).

Macdonald answered 19/4, 2011 at 2:52 Comment(2)
So, If we change provisioning profile of our application, would it be able to access previously stored values in keychain.Grandiose
@MoazSaeed from my experience, the app have access to previously stored values in Keychain even if the provisioning profile changes.Centenarian
A
30

For those looking for a Swift version of @amro's answer:

    let userDefaults = NSUserDefaults.standardUserDefaults()

    if userDefaults.boolForKey("hasRunBefore") == false {

        // remove keychain items here


        // update the flag indicator
        userDefaults.setBool(true, forKey: "hasRunBefore")
        userDefaults.synchronize() // forces the app to update the NSUserDefaults

        return
    }
Amblygonite answered 24/10, 2015 at 14:18 Comment(2)
How about old app version? For all of them the value won't be there potentially if you manage a logged session all the users might be logged out.Lavadalavage
I don't understand what you mean.Amblygonite
D
11

C# Xamarin version

    const string FIRST_RUN = "hasRunBefore";
    var userDefaults = NSUserDefaults.StandardUserDefaults;
    if (!userDefaults.BoolForKey(FIRST_RUN))
    {
        //TODO: remove keychain items
        userDefaults.SetBool(true, FIRST_RUN);
        userDefaults.Synchronize();
    }

... and to clear records from the keychain (TODO comment above)

        var securityRecords = new[] { SecKind.GenericPassword,
                                    SecKind.Certificate,
                                    SecKind.Identity,
                                    SecKind.InternetPassword,
                                    SecKind.Key
                                };
        foreach (var recordKind in securityRecords)
        {
            SecRecord query = new SecRecord(recordKind);
            SecKeyChain.Remove(query);
        }
Deppy answered 2/3, 2018 at 8:16 Comment(1)
By using if (VersionTracking.IsFirstLaunchEver) {// remove keychain items} from Xamarin.Essentials you do not need the code for the userDefaults. Xamarin.Essentials wraps that for you.Aplacental
S
8

Files will be deleted from your app's document directory when the user uninstalls the app. Knowing this, all you have to do is check whether a file exists as the first thing that happens in application:didFinishLaunchingWithOptions:. Afterwards, unconditionally create the file (even if it's just a dummy file).

If the file did not exist at time of check, you know this is the first run since the latest install. If you need to know later in the app, save the boolean result to your app delegate member.

Shikoku answered 14/9, 2013 at 2:19 Comment(0)
F
8

@amro's answer translated to Swift 4.0:

if UserDefaults.standard.object(forKey: "FirstInstall") == nil {
    UserDefaults.standard.set(false, forKey: "FirstInstall")
    UserDefaults.standard.synchronize()
}
Fancy answered 9/5, 2018 at 7:55 Comment(1)
Or even if !UserDefaults.standard.bool(forKey: "FirstInstall") which defaults to false if the key doesn't exist. And .synchronize() not needed.Barret
C
2

This seems to be the default behavior on iOS 10.3 based on behavior people have been witnessing in beta #2. Haven't found any official documentation about this yet so please comment if you have.

Chromogen answered 21/2, 2017 at 7:39 Comment(1)
It was until beta 5 i guess, public release of iOS 10.3 does not contain this change.Ragg
B
0

Just add an app setting bundle and implement a toggle to reset the keychain on app restart or something based on the value selected through settings (available through userDefaults)

Baucom answered 14/12, 2020 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.