I am migrating our organisation's app to a new developer account, for internal reasons. I've learned that upon transferring the app to the new account, and after we release an update for the app from the new developer account, we will incur a one time loss of keychain data.
My concern is, that we cannot afford this loss of data. Due to security reasons and nature of the type of clients using the app.
So my question is 2 part;
1) Is there any way that I can prevent this keychain data loss?
2) If not, what are the alternate strategies I can use to prevent the users from getting logged out during this transition?
Assuming you're talking about a real, full App Transfer I don't think this is possible. Keychains are not just differentiated via the app ID, but also the organization's ID (or more precisely the organization's ID is part of an app ID). Since the app's ID de facto changes with a transfer, iOS will prevent it to look up its old keychain. Not even app groups get around this, as a group cannot consist of apps by different organizations AFAIK. Apple would need to implement a kind of exception to this for at least a certain "migration period", but I doubt that will ever happen (for security considerations).
The only way to circumvent a re-login so that the transferred app can save the credentials in its "new" keychain would be to first have an update for the old, not-yet transferred app and have it save the credentials elsewhere, then have the transferred app look them up from there. I'd strongly recommend against this for security reasons, but if you absolutely must do that, you could upload the credentials somewhere secure (via an encrypted connection, obviously, i.e. SSL). You might even be able to use shared web credentials for this, but I haven't looked into that too deeply, so I am not sure whether both apps can be configured to be associated with that website (they're coming from two different teams, after all, but it might be allowed).
Even then, some users might "miss" the update of the older app before the transfer (and update to the "new" one), then they would have to re-login anyways.
If you follow this idea, be aware that you're "moving around" users' private data in a new way! Considering the recent GDPR fuzz this might even have string legal implications, so check with your company's legal advisors about this, too!
1) There is no way your can prevent data loss, since the App ID Prefix has changed during app transfer TN2311 > A one-time loss in keychain data will occur if you switch your App ID prefix
2)
If you application utilizes Push Notification, you can use the APNs device tokens to remap the user account.
Once the new app is launched:
Send the device token to server
Find the username from your database
Generate a session token (add your own security design here)
Save username + the token in your new Keychain
For those who doesn't allow notification, just force them to relogin
Related
I transferred an app to a new developer account. When I upload a build from the new developer account and deploy it through Testflight, I encounter that my users logged out from the app.
I think this is due to the Keychain Loss, because the Team ID changed. So, I contacted Apple, and they said that there is no way to have access to the old Keychain in the new developer account, but that I could transfer the app back to my old developer account.
This is really bad for us, since we have user-created data in the device that gets lost on logout. We also have anonymous users, and they can’t even regain access to their accounts because there are no credentials to login. This is not acceptable to us because we would be losing user data, but we also need to transfer the app to the new developer account... So we are trying to find possible solutions.
Possible Solution #1
Far from ideal: keep using the old developer account for a month, for example, and show an alert saying something like:
Please save/backup all your data and signup (if you have an anonymous
account) because all unsaved data will be lost on the [todays date + 1
month] due to a migration. Sorry for the inconvenience.
After a month, we transfer again to the new developer account. Users that didn’t see the message in that month or didn’t save data, bye bye data… terrible.
Possible Solution #2
I'm not sure if Firebase Auth iOS SDK has the APIs to do something like this:
Transfer the app back again to the old developer account
Upload a new build from there that saves the Firebase Authentication State (e.g. the token) temporarily in a file (I know it's insecure... maybe I could encrypt it)
Transfer the app back again to the new developer account
Upload a new build that checks if that file exists, and copies the Auth State into the Firebase Auth SDK again. This way the user is still logged in as if nothing happened.
This second solution is kind of insecure, complicated and difficult to test, but from the user point of view, it would be more ideal than the first solution (if everything goes well).
So, specifically my questions are:
Would solution #2 be possible with Firebase Auth?
Does anyone have more ideas on how to approach this? I guess that transferring apps (with login) is a common use case!
We currently only use Anonymous & Email/Password authentication methods.
Thanks!
So, I didn't find an ideal solution.
I ended up doing a mix of solutions #1 and #2:
I transferred the app back to the old apple account
I uploaded a new build that:
a. extracts keychain data (which has login credentials and other stuff)
b. encrypts this data (I used CryptoSwift)
c. saves it into a file in the documents directory
Waited 1-2 months so users can open the app to perform this first part of the migration
While waiting, we sent emails and push notifications asking users to backup their data, saying that a big migration was coming soon, with the excuse that we "grew" a lot lately and we were scaling stuff :P
Transferred the app to the new apple account
I uploaded a new build that:
a. checks if the migration file exists (in the documents directory)
b. if exists, read the migration file and decrypts data
c. puts this data into the keychain (which is empty after app transfer)
d. deletes the migration file from the documents directory
Left this migration code for some more months so people open the app and perform migration
Delete code, migration period is over.
I sent logs to the backend to see if migrations were successful or not. So far, users are all migrating correctly.
There are still cases where users could lose their data, for example, when anonymous users didn't perform the first part of the migration. But I can't think of a better solution... so far data loss is minimal. But it also took us some time to get things right and do all the proper testing.
If someone in the future has a better solution to this issue, please let me know!! I'm curious.
I hope the answer helps someone.
I think the Apple rep was wrong. Your keychain is tied to a bundle ID, not a Team ID.
Suppose for example that you transferred SomeApp from the original app owner, OldCo, to NewCo. The app comprises a custom keyboard, the actual SomeApp app, and an App Group. You have some Identifiers set up on developer.apple.com:
App ID:
com.OldCo.SomeApp
com.OldCo.SomeApp.keyboard
App Group ID:
com.OldCo.SomeAppGroup
If you change these to start with com.NewCo., users will lose their iCloud data. But if you leave them as com.OldCo., all should be well. Note that for you to create these IDs in NewCo's developer.apple.com account, OldCo will have to remove them from their account. It feels weird to have NewCo owning IDs with OldCo in them, but life is weird.
If I'm missing some nuance whereby the change in TeamID affects the keychain, please let me know, and I'll do more research.
I have implemented synchronised keychain storage in my App with the SecKeychain API using kSecAttrSynchronizable. Am I correct in thinking that iCloud keychain must be enabled (iOS settings > iCloud > iCloud keychain) for this to work?
If so, is there any way to tell if this is enabled?
The kSecAttrSynchronizable attribute is used by your app to tell keychain if it is desired behavior that this keychain entry is synced to other Apple devices of that user or not.
Why would you desire an item not to be synced? E.g. the user doesn't want it to be synced as he wants to use a different password on his iPhone and on his iPad; so you may like to offer a pref and let the user chose if he desires syncing this password. Or syncing this password is simply not meaningful, e.g. it is a password that is used to encrypt local data of your application, local data that will only be present on the current device and thus this password is of no use to other devices, they lack the data for decryption.
However, your app can only express the wish for your item to be synced, whether syncing takes place at all is up to the user. The user decides if he wants password syncing at all, after all password syncing requires the passwords to be stored on Apple's servers. They are stored encrypted and not even Apple themselves have access to the password data, yet some users still don't have a good feeling having when that kind of information lying around on public servers on the Internet. So unless a user enables syncing, no passwords are synced.
Why would you need to know if the user has enabled it? To tell the user that passwords are not synced unless he enables syncing of passwords? Sounds a bit like telling the user that the room is dark, unless he turns on the light. If it is not on, the user may have chosen so on purpose, you cannot know if he did, can you? If you offer an option for password synchronization in the app prefs and the user enables is, just show an info text below that this feature requires iCloud keychain syncing to be enabled or else will have no effect.
Other than that, if you don't offer such an option, it should still be pretty obvious to all users that when your app promises "Now supporting iCloud Keychain Synchronization" that this feature will only work if "iCloud Keychain Synchronization" is enabled, don't you think? So to my best knowledge, there is no API that will directly tell you. One may detect in indirectly through other means but I don't recommend such kind of hacks, they are very fragile and sooner or later break with an update.
A while ago, I got banned from an iOS app. Not a user ID ban, but it seems to have been a device ban. Even if I delete and reinstall the app and try to make a new account, it would automatically get banned. My question is, how are they doing this? What are all the possible ways to ban a device that is persistent even after deleting and re-installing the app?
It is not possible to access UDID anymore, so there's that. And I don't think iOS allows them to view iTunes account, so that's not possible. Are they perhaps storing anything in the keychain? From what I know, some things in the keychain don't get removed even if the app is uninstalled.
I'd like to implement something like this in my app, so I want to know all the possible methods, with pros and cons for each.
When the app is uninstalled, all data goes with it. So writing anything to disk isn't going to work.
You can store something in the keychain, although users can edit the keychain if they sync it to a mac. That makes it pretty insecure.
The best option is to store it off the device, in iCloud. I'd go with key/value storage: https://developer.apple.com/library/mac/documentation/General/Conceptual/iCloudDesignGuide/Chapters/DesigningForKey-ValueDataIniCloud.html
Another option you could choose is Apple's replacement for reading the UDID which is to grab the advertising identifier. However it is possible for users to block this in Settings and your app could theoretically be kicked out of the store if it's used for something other than the intended purpose. I don't think it's actively policed, but still probably not a good idea to use it.
A belts and suspenders option would be to do everything:
save it to disk (in application support)
save it to key value storage in icloud
save it to keychain
use the advertising identifier to and send it to a remote server that you control (personally I'd skip this to avoid having the app banned from the store)
However... I'm not sure if Apple allows users to be banned. You might well be violating the developer rules by doing so. Especially the last one, which goes something like "this list is incomplete and constantly changing, your app might cause us to add a new rule to the list".
If it's a paid app or has in app purchases I'm pretty sure they would issue refunds to any customer who complains, and they'd probably follow the refund up with kicking your app out of the store.
Were I tasked to implement something like this, I would do exactly what you suspect: generate a simple random token, keep it in the keychain, and include it in API requests.
Nothing in the iOS keychain is removed when the app is uninstalled. I don't actually know of a good way to manually remove stuff from it.
To contextualize the question, our use case is an app through which users can get free promotional items on signup. We would like to prevent abuse of the system by limiting to one promotion per device. Since we can't access the UDID, we need some other (mostly) reliable way to check if the phone has already signed up an account. We don't need a solution that is impossible to circumvent, just one that is highly inconvenient to circumvent.
If we store a unique key in the user's keychain, then we'll be able to read it again even if they uninstall and reinstall the app. We're considering using this method to track devices that have already signed up accounts.
Questions:
Is there a relatively easy way that the user could delete or change our app's keychain data? You can assume that the user's device is not jailbroken, and that they will not go to the trouble of completely reinstalling the OS.
What are other options besides using the keychain we might consider? Keep in mind that the app communicates with our server during signup, so we can store previous signup information of any kind on the server.
Are there any gotchas or problems with the keychain method we should know about?
I had an iPhone app developed by a company, which contains consumables purchases, specifically a currency named as "coins", used for app-specific purchases. In the current version, the team has added the ability for a user to register under a username/email/password. This information is stored on my external server and used as a recovery for the user's purchased coins via signing in using these credentials.
The model looks fine programmatically but is not convenient in terms of usability. Ideally, i would want my users to not have to register at all. I have been reading that it is a possibility to store the user's purchased consumables on the client side, using the internal keychain.
Now this would imply that consumables can only be restored on this single device (i guess unless iCloud is used). But is this allowed by Apple ? Or there should be a method to restore across different devices ?
If the keychain method is allowed, I believe that it would be way better for my app to go with that. I just wanted to make sure that Apple would not have a problem with that. I have been reading that it's allowed, but do not want to have unpleasant surprised after changing the whole client-server model to client-only keychain.
If you have experience with that, could you please let me know what the case is ?
EDIT : Somewhere on SO I read that using Gamecenter's GKPlayer class is also a way to store information on my server and retrieve that automatically in order to identify a user purchase. This would mean that the step of username/email registration is avoided, which is something i really really want to avoid. Is this really a possibility that is allowed by Apple ?
EDIT 2 : I am now thinking that I will keep the client-server model but lose the username/password and use the device id instead. This way i will be able to keep the restoration of coins working on a specific device. Would that be ok you think?