Preventing reuse of free periods

I am looking for a way to limit the reuse of a one-time offer in an app. For example, a feature that is available for a period of time and subsequently must be purchased through an IAP. I am concerned that the 'free trial' period in a 'free' non-consumable IAP or an autorenewable subscription can be 'hacked' by creating multiple App Store accounts. (Is that concern unwarrented?) While I am concerned about multiple App Store accounts, I am not concerned about multiple Apple ID's because it is a sufficient pain to change a device's Apple ID that I can allow that 'hack'. I do not need to know who the user is - I just need a permanent identifier for the user (or the device) that records 'first use of app' by the user or by the device. (DeviceCheck requires a server and is just a bit too complicated.)


1) I can't use identifierForVendor because it gets reset on app delete-and-reinstall.

2) I am told I can no longer reliably use the keychain (my current solution) because in various betas and perhaps the latest iOS it gets wiped out on app deletion.

3) I am told that the user's iCloud key-value can be deleted by the user through the Settings app after app deletion.


A) Can I use the user's CloudKit private database as a place to store permanent information? Will it survive deletion of the app and the user removing the app from being able to access CloudKit?


B) Can I use the user's userRecord in the public database in CloudKit? Will it survive deletion of the app and the user removing the app from being able to access CloudKit?


C) Is there any other way to leave a permanent record on a user's device or elsewhere?

Accepted Reply

A) Can I use the user's CloudKit private database as a place to store permanent information?

Yes, but this associates the info with the user, not the device, which isn’t what you’re angling for.

B) Can I use the user's userRecord in the public database in CloudKit?

Ditto.

C) Is there any other way to leave a permanent record on a user's device or elsewhere?

That’s a very broad question, so I can only give a general answer.

Apple’s general take on this is that the user should be in charge of all the persistent identifiers you might use for this sort of thing. The only planned exception to this is DeviceCheck. You can also use the keychain but, as I explained in the post I referenced above, Apple has tried to close that loophole in the past, and you can reasonably assume that we will eventually succeed.

To reiterate, DeviceCheck is exactly what you’re looking for here. Use it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

I think I was responsible for #2. I was thinking strictly from a macOS perspective. I don't know about iOS. However, I know that many people know lots about iOS and can do all kinds of funky things with it, whether permitted by Apple or not. One of the reasons jailbreaking is so popular is because it makes these kinds of things easy.

DeviceCheck is the right approach here; it’s exactly what it was designed for. Requiring a server is a pain point, but my experience is that it’s common for third-parties to create infrastructure to ease such pain.

Other approaches tend to rely on undocumented behaviour. For example, you wrote:

2) I am told I can no longer reliably use the keychain (my current solution) because in various betas and perhaps the latest iOS it gets wiped out on app deletion.

You are correct to be wary of this. See this post for some background on the issue.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks. Can you comment on:


A) Can I use the user's CloudKit private database as a place to store permanent information? Will it survive deletion of the app and the user removing the app from being able to access CloudKit?


B) Can I use the user's userRecord in the public database in CloudKit? Will it survive deletion of the app and the user removing the app from being able to access CloudKit?


C) Is there any other way to leave a permanent record on a user's device or elsewhere?

A) Can I use the user's CloudKit private database as a place to store permanent information?

Yes, but this associates the info with the user, not the device, which isn’t what you’re angling for.

B) Can I use the user's userRecord in the public database in CloudKit?

Ditto.

C) Is there any other way to leave a permanent record on a user's device or elsewhere?

That’s a very broad question, so I can only give a general answer.

Apple’s general take on this is that the user should be in charge of all the persistent identifiers you might use for this sort of thing. The only planned exception to this is DeviceCheck. You can also use the keychain but, as I explained in the post I referenced above, Apple has tried to close that loophole in the past, and you can reasonably assume that we will eventually succeed.

To reiterate, DeviceCheck is exactly what you’re looking for here. Use it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks. A or B work for me. This will limit the use of a 'single use offer' to an AppleID - and that is fine for my purpose. If a user buys another device they do not get another 'single use offer'. If a user decides to switch AppleID - then they have succesfully spoofed me. I can live with that.


Regarding DeviceCheck, my (and I suspect many other developers') problem is this:

"Each request you send to the query and update endpoints must include an authorization header that contains your authentication key. The authentication key must must use the ES256 algorithm and be in the Base 64 URL–encoded JSON web token format. If your token doesn't use this format, you receive a

BAD_AUTHENTICATION_TOKEN
HTTP error.

In the

curl
command you create to send a request, you specify the authorization header and include your authentication key using
Bearer \
, like this:

-H "
Authorization: Bearer <GeneratedJWT>
" \


I can't parse it. I could try to do DeviceCheck directly from the app (albeit https versus man-in-the-middle) but I can't encompass that "Each request..." paragraph into an Objective C command.

How do you handle this properly if user rolls back timestamp ?

Thanks!