App transfer with Developer ID

Hello,

I have a multi-platform app that is split across two organizations:

  • One is on iOS, and is distributed using the App Store with bundle ID X with team ID A.
  • One is on macOS, and is distributed using Developer ID with bundle ID Y with team ID B.

Once again, these are in two separate organizations. To consolidate these accounts we'd like to transfer ownership of Y to team ID A. However, according to the app transfer criteria, it appears that that's not possible:

  • Both the transferor and recipient accounts can’t be in a pending or changing state, and the latest version of their paid and free agreements must be accepted.

[...]

  • The app must have had at least one version that's been approved for distribution.

Given the context from the rest of the page, it seems valid to assume (and I've confirmed this through speaking with technical support) that apps are only eligible for transfer if they've been submitted to the App Store, so I'm considering looking into it just for the purposes of this transfer.

This app has a fairly large user base and if possible we want to avoid any user disruption (and any cost inflicted on our API) as a result of a forced logout due to losing access to the previous keychain.

As a bonus, it would be nice, though not necessary, if the macOS app could ship under the same entry as the iOS app. As I understand it, this would require changing the macOS app to use bundle ID X.

Before going down this road, I'd like to confirm if the following plan is a sane one for accomplishing a complete app transfer that satisfies the above requirements:

  1. Distribute the app on the macOS App Store under team ID B.
  2. Transfer the app, and continue distribution on the macOS App Store under team ID A.
  3. Obtain a new Developer ID certificate for using bundle ID Y with team ID B.
  4. Resume distribution of the Developer-ID-signed app with team ID B, without loss of keychain access.

If loss of keychain access is not possible, can someone confirm if it is at least possible to keep the same bundle ID after performing the steps above?

Many thanks in advance for your help - there is much conflicting information online and in this forum, and little documentation when it comes to Developer ID transfers. I've even spoken to several Apple employees who have directed me here.

Replies

In general, there isn’t a good answer to the problem of transferring a Developer ID app. See this thread for the details. However, your case is more complex than normal, so I want to clarify a few points.

You wrote:

To consolidate these accounts we'd like to transfer ownership of Y to team ID A.

Is the final plan to ship Y from the Mac App Store? Or to distribute Y directly with Developer ID signing?

This app has a fairly large user base and if possible we want to avoid any user disruption (and any cost inflicted on our API) as a result of a forced logout due to losing access to the previous keychain.

Is your app using the file-based keychain? Or the data protection keychain? See TN3137 On Mac keychain APIs and implementations for an explanation as to what these terms mean.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

As far as I can tell, we're using the SecItem APIs, mostly provided by this package, which would imply the data protection keychain.

Is the final plan to ship Y from the Mac App Store? Or to distribute Y directly with Developer ID signing?

The latter, but we see the former as a bonus since it's something we were thinking about doing anyway.

we're using the SecItem APIs

The SecItem API can talk to either the file-based or the data protection keychain.

mostly provided by this package

I had a quick look and I wasn’t able to see any indication in that code that it forces the use of the data protection keychain.

It’s important that you nail this down because, if you’re using the data protection keychain, there really is no way to avoid the loss of keychain items [1].

A good test is to run Keychain Access and look at the keychain items your app created. Check whether they’re in the login keychain or iCloud Keychain (aka Local Items, if you have iCloud Keychain disabled).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Changing teams necessitates a change in App ID prefix, and thus you lose access to your current keychain access groups. I talk about this in detail, albeit in in an iOS context, in App ID Prefix Change and Keychain Access.

I checked and the items are going in the login keychain, which to me would suggest that it's using the file-backed keychain. Is this correct?

Is this correct?

Yes. And that is indeed good news.

Your new and old apps will the same bundle ID but different Team IDs. This means they’ll have a different designated requirement (DR). See TN3127 Inside Code Signing: Requirements for more info about that.

That means that the new app won’t be able to access privacy-protected resources based on the old app’s DR. You’ll see two different behaviours:

  • For TCC stuff, so the things in System Settings > Privacy & security, the system should prompt the user again and then remember the user’s decision. This is pretty reasonable behaviour, but you might want to tweak your first-run experience to set user expectations.

  • For the keychain, if the new app reads a keychain item created by the old app, it’ll get a keychain alert [1]. It’s going to be hard for the user to understand what this means, so your first-run experience is important here. Obviously you know your app better than I do, but in your shoes I might include a ‘import credentials from old version’ panel in the first-run experience with an explicit Import button and some text explaining that they’ll be prompted to allow the import and a screen shot of what to expect.

This is going to require some careful testing (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Historically you could’ve avoided this alert by having the old app create the keychain item with a custom ACL. This is no longer feasible because the keychain now always prompts in the cross-team case.

Indeed, and I'm feeling pretty intimidated. Maybe you would also be able to inform me about the possibility of grouping the application under the same entry as the iOS one in account A, after an additional step if necessary?

As I'm reading the TN, it seems like the step from Dev ID -> App Store, to Dev ID again suggests that all of them will have different DRs and thus won't be able to interoperate. Is there no affordance that lets me store app credentials under one access policy?

Maybe you would also be able to inform me about the possibility of grouping the application under the same entry as the iOS one in account A, after an additional step if necessary?

Honestly, I’m not 100% sure how that’ll pan out. The standard path here is to create a universal App ID, that is, the same App ID for all your platforms. You then submit different builds for each platfrom.

However, that’s not the only way to do this. At a minimum, it’s possible to group a Mac Catalyst app with its iOS equivalent even though they have different App IDs.

However, I don’t think there’s a general way to do that. IIUC, the Mac Catalyst exception is rather tight.

Is there no affordance that lets me store app credentials under one access policy?

Once you have both apps under the same team, you can set up mutually compatible DRs. I recently updated TN3127 to discuss that.

Also, once the apps are under the same team the keychain access problem goes away because you can switch to the data protection keychain.

It’s probably possible to craft a DR that’s compatible with both your the Developer ID app from your old team and the Mac App Store app from your new team. That should help with TCC but I don’t think it’ll help with keychain. The issue is that the file-based keychain has additional checks that prevent cross-team sharing. I touch on this topic in this post.


Finally, I want to put a plug in for an entirely different approach, which is to create a new app for the new team. This has a number of advantages:

  • The new app has a number bundle ID, so it’s starting from scratch from a code signing perspective [1]. You get to leave all the baggage behind.

  • The user can install both apps, allowing a safer upgrade path. If something goes wrong, they can revert to using the old app until you have a chance to fix the upgrade path.

  • To ease the transition you can add an ‘import from old version’ feature to your new app. And you can craft that UI to prepare the user for whatever TCC and keychain alerts that it triggers.

  • You can update your old app to actively ‘advertise’ the new app.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] You can re-use all the code, of course.