Is there any way to get the transaction in its original transaction in a secure format (such as a JWT) using the StoreKit 2 API?
I can see the [jsonRepresentation] (https://developer.apple.com/documentation/storekit/transaction/3868410-jsonrepresentation) in the Transaction object, but that is already decoded.
The transaction needs to be sent to the backend server for further processing, but there is no way to verify the integrity of the transaction details. For instance, there is no way to know if the transaction details the backend server receives actually belong to that specific user. The backend server can query the App Store API for a specific transaction ID, and then compare the details, but that still does not prove that the transaction the client sent actually belongs to that client.
What I'm after is any way to send a transaction to the backend server that has been signed by Apple, which can then be validated.
There a few use-cases for this, but the most pressing one is that of recovering purchases and linking new devices to an existing app account. For example:
User A installs the client app on device X, which creates an account on the backend server. Assume there is no login-mechanism in place, we just need to rely on their in-app purchase history to identify a user account on new devices (or a re-installation of the app on an existing device).
User A purchases a non-consumable or subscription in this app. Now that user has a transaction history that can be retrieved from the App Store API.
User A now installs the app on device Y. The backend server creates a new account for this user (since it does not know who that user is, at least not yet). The user triggers a "recover purchases" command in the app, so the app queries the StoreKit 2 API for existing transactions. Store Kit responds with the previous transaction, which the app then sends across to the backend server (currently using jsonRepresentation
). The backend server does not have a way to validate that the provided transaction data actually belongs to user A, so simply trusts it, and queries the App Store API with the provided transactionID
. The server receives the transaction from the App Store API, and can validate that it in fact is a valid transaction, but still, there is no way to be certain that it belongs to user A. The backend server then merges the newly created account on device Y with the existing account on device X, so device Y now access that same initial account.
I feel that there is a problem here. Let's say a malicious user B somehow has access to valid client transaction data from user A. User B installs the app on device Z. User B transmits a a valid jsonRepresentation
of a transaction that belongs to user A to the backend server to link their device to the existing account. The server queries the App Store API with the transaction ID, and validates it, and given that this is successful, it simply links device Z to the existing account of user A. Now user B has access to the account of user A.
The chances of this happening could be reduced (but not completely avoided) if there was a way to send a signed transaction from a device to the server. That way the backend server could verify the source of the transaction coming from the device, and if it is successful, continue on with any further processing.
If there is something in the existing jsonRepresentation
of the transaction from the client that I could use for verification, then please point me in the right direction.
Yes, all of this may be avoided if the application has login accounts, but that isn't the issue here. Our app does have the ability to log in via 3rd party providers, but that is entirely optional, so the only way we can restore accounts on new devices is to check the App Store transaction history.
Any help or suggestions regarding this would be appreciated.
Hello, thank you for reaching out. As you have pointed out, passing the signed JWS from client to server is very very important in StoreKit 2, and is in fact the highly recommended way to send data from client to server. Once you have it on your server you can very it yourself or use the App Store Server Library to validate and decode it for you. Back to your original question, https://developer.apple.com/documentation/storekit/verificationresult/3868429-jwsrepresentation
The field is jwsRepresentation on the verification result itself.