Apple sign-in migration on AppStore app transfer
Published: 2023-09-13 04:16:57

When transferring an app that uses 'Sign-in with Apple' to another App Store account, the ID value (Team scoped identifier) used to distinguish 'Sign-in with Apple' users within that app changes. Not only this value, but if the original user activated the email hiding (private email) feature, the email address created by it also changes. Therefore, a separate migration must be performed before and after the app transfer (app transfer). If not handled properly, an already registered user trying to log in may not be recognized as the original user but could be treated as a new user. Careful handling is required.

General procedures for app transfer are well documented in [this official document](https://developer.apple.com/help/app-store-connect/transfer-an-app/overview-of-app-transfer/). In this article, we will focus on the steps needed to transfer an app that uses 'Sign-in with Apple' to another account.


## Preparation before app transfer

### Storing transfer identifier

As mentioned earlier, before and after an app transfer, when a 'Sign-in with Apple' user, even if the user logs in with the same Apple ID, the team scope identifier they receive before the transfer and after the transfer will differ. The team scope identifier refers to the value in the `sub` field of the token provided during 'Sign-in with Apple', which is used to distinguish 'Sign-in with Apple' users within the app. Because this value changes, we need mapping information that can connect the team scope identifier from before the transfer to the one after the transfer. This mapping information is known as the transfer identifier.

To obtain the transfer identifier, the following information is required:

1. Team ID of the account before the transfer
2. The `.p8` Key file linked with Apple Sign-in from the pre-transfer account
3. The app's bundle ID that is being transferred
4. Team ID of the account after the transfer

To get the transfer identifier, you need to call Apple's migration API at `/auth/usermigrationinfo`. To have the permission to call this API, you first need to call the authentication API at `/auth/token` to get an access token.

To get the access token, you have to create a `client_secret` using the information from items 1, 2, and 3 above. Then, by calling the authentication API using this `client_secret`, you can obtain the access token in the response.

With the access token acquired, you can call the migration API to ultimately get the transfer identifier. You can save this value in the user DB and use it after the transfer.

Explaining the process in words may seem complicated, but seeing it in actual code can make it much simpler.


```
async function main() {
    // TODO: Modify the following parameters to your own
    const sendingTeamId = 'S12341234P'
    const sendingTeamKeyId = 'T12341234L'
    const sendingTeamKeyFilePath = path.join(__dirname, 'Example_AuthKey_T12341234L.p8')
    const clientId = 'com.example.app'
    const receivingTeamId = 'R12341234P'

    // Just one appleId is used here for example. You usually should loop through all the users with Apple Sign-in.
    // Team scoped identifier for each user
    const appleId = '000506.5951a85d72c445918250badf39181d0f.0331'

    const clientSecret = generateClientSecret(sendingTeamId, sendingTeamKeyId, sendingTeamKeyFilePath, clientId)

    const accessToken = await getAppleApiAccessToken(clientId, clientSecret)

    const appleTransferId = await getAppleTransferId(clientId, clientSecret, accessToken, appleId, receivingTeamId)

    // Store this mappings in your user database before transfering app.
    console.log(`TransferID for ${appleId} is ${appleTransferId}`)
}
```

Please refer `before-app-transfer.js` file in [apple-signin-migration-on-app-transfer](https://github.com/yjiq150/apple-signin-migration-on-app-transfer) for the entire codes.


### Modify server code for Apple sign-in

As soon as the app transfer is completed, when 'Sign-in with Apple' users sign in, the `sub` field within the token they receive will contain the team scope identifier based on the transferred account. Additionally, for 60 days, the `transfer_sub` field will contain the transfer identifier that we saved in the previous step.

After the app transfer is completed, there will be a migration process (which will be explained later) to update all the team scope identifiers based on the transferred account. However, applying this update instantly to all users at the exact moment when the app transfer completes is impossible. This is because while this update is ongoing, existing users might attempt to sign in using 'Sign-in with Apple'.

Therefore, it's necessary to modify the server code in advance so that at the moment of login, the system can find the user in the DB matching the `transfer_sub` value and then update that user's pre-transfer team scope identifier to the post-transfer value. Additionally, if a user has activated the email hiding feature, their email address will also change, so it's crucial to remember to update this as well.

With these steps completed, all preparations are done. Now, you can proceed with the app transfer through the App Store Connect.

## Migration task after app transfer

The `transfer_sub` field mentioned earlier, provided in the token during sign in, is only available for 60 days after the app transfer. After this period, it is no longer included in the token. Therefore, users who have not logged in within these 60 days after the app transfer won't be properly migrated. For such users, there needs to be a mechanism on the server-side to execute a batch operation, calling Apple's migration API directly, to fetch the team scope identifier based on the transferred account and update the user information in bulk.

To retrieve user information based on the transferred account, the following details are needed:

1. Team ID of the account after the transfer
2. The `.p8` Key file associated with Apple Sign-in of the post-transfer account
3. Bundle ID of the transferred app
4. Transfer identifiers for each user, saved before the app transfer

To get the transfer identifier, you need to call Apple's migration API at `/auth/usermigrationinfo`. To have the permission to call this API, you first need to call the authentication API at `/auth/token` to get an access token.

To get the access token, you have to create a `client_secret` using the information from items 1, 2, and 3 above. Then, by calling the authentication API using this `client_secret`, you can obtain the access token in the response.

With the access token acquired, you can call the migration API to ultimately get the transfer identifier. You can save this value in the user DB and use it after the transfer.

Explaining the process in words may seem complicated, but seeing it in actual code can make it much simpler.

```
async function main() {
    // TODO: Modify the following parameters to your own
    const receivingTeamId = 'R12341234P'
    const receivingTeamKeyId = 'S12341234L'
    const receivingTeamKeyFilePath = path.join(__dirname, 'Example_AuthKey_S12341234L.p8')
    const clientId = 'com.example.app'

    // Just one appleTransferId is used here for example. You usually should loop through all the users with Apple Sign-in.
    // Use 'transfer identifier' generated in 'before-app-transfer.js' for each user
    const appleTransferId = '000506.5951a85d72c445918250badf39181d0f.0331'

    const clientSecret = generateClientSecret(receivingTeamId, receivingTeamKeyId, receivingTeamKeyFilePath, clientId)

    const accessToken = await getAppleApiAccessToken(clientId, clientSecret)

    const { sub: appleId, email, is_private_email } = await getUserInfoByTransferId(clientId, clientSecret, accessToken, appleTransferId)

    // Update your user info in database with the new information.
    // When user enables private email for Apple Sign-in, the email is also different after transfer.
    console.log(`The new user information for ${appleTransferId} is ${appleId} / ${email} / ${is_private_email}`)
}
```

Please refer `after-app-transfer.js` file in [apple-signin-migration-on-app-transfer](https://github.com/yjiq150/apple-signin-migration-on-app-transfer) for the entire codes.