Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Why not use transactionId as the user identifier? #2

Open
atacan opened this issue May 12, 2024 · 3 comments
Open

Question: Why not use transactionId as the user identifier? #2

atacan opened this issue May 12, 2024 · 3 comments

Comments

@atacan
Copy link

atacan commented May 12, 2024

First of all, thank you for the great work, and sorry to hear the financial loss that you had.

I was wondering why transaction ID was not used as a user identifier instead you rely on app account token that is set by the app during the purchase. Isn't originalTransactionId also unique by an Apple account that made the purchase?

I thought wherever I save it, the user can delete the app account token. UserDefaults, Keychain, Application support etc. can easily be manipulated by the user on macOS.

@ronaldmannak
Copy link
Collaborator

That's a great question. The app account token is not stored on the client, but on Apple App Store servers. These are the steps:

  1. Client creates a unique UUID
  2. Token is passed to Apple's App Store servers using product.purchase()
  3. The proxy fetches the the app account token from the App Store Servers

I could be wrong, but I think there are edge cases where the originalTransactionId isn't available, for instance when not the original transaction receipt is available. I'm also not sure if originalTransactionId works in case of family subscriptions. In both cases, app account token does work.
The app account token is used for the rate limiter. In case no app account token is set, you can disable the rate limiter using the environment variables by setting enableRateLimiter to 0.
Note that the app account token is a different token than the JWT session token, which is stored on the client side. The session token is just a convenience token so the proxy doesn't have to make a slow call to the Apple App Store Server for every call.

@atacan
Copy link
Author

atacan commented May 25, 2024

Thank you.

The transaction id that the client puts in the fetchToken request's body is this TranSaction id https://developer.apple.com/documentation/storekit/transaction/3749696-id, right?

I see that it's a UInt64. I haven't had any real transaction ID, could you tell me whether the following concern makes sense?

If this id is a naively incremented integer like 1,2,3... for each transaction, then the attacker can easily try a few integers and find one that would correspond to an active subscription's transaction ID. They can call the /appstore endpoint whenever they need to get a new token.

@ronaldmannak
Copy link
Collaborator

@atacan: I've updated the in-app purchase validation and Pico Proxy now verifies the JWS representation of StoreKit 2's Transaction. The previous step where Pico Proxy calls the App Store Server is no longer necessary. See for more details: https://medium.com/p/98626641d3ea

The AppStoreAccount token is still used to identify the original purchaser so that there is one rate limit for the whole family (in case the developer enabled family sharing).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants