Hi @SwiftedMind,
From what I read in the documentation, there might be a way to do this, by sending the users App ID (generated by the RC SDK) to the backend on each request and have the backend call the RC API with that App ID to check the subscription status. I have not tested this because I am not at the stage of development yet.
This would be a good way to do this. You can call our GET /subscribers endpoint rather frequently as it’s designed for this level of usage. I recommend to have some sort of cache so you’re not hitting the endpoint for the same user for every request they make for performance reasons - for example our SDK caches the API’s request for 5 minutes as it’s unlikely anything will change in our system in 5 minutes. Hitting your own database for the info will likely be quicker than our API.
Awesome, thanks a lot for the answer! Caching is also a good idea, I will do that
I have the same requirement - authenticating requests to my own API made on behalf of a customer.
I don’t understand how simply sending the customer’s App ID in the request provides authentication - it would be easy to find out another customer’s ID and then anyone can use it to make requests?
But how would you find other people's App ID? The person owning that ID would have to share it with someone. But then they could just as well share their username and password, if you had a registration-based system instead.
In general, sure, you need to be aware of that possibility and try to safeguard against it as much as possible (though nothing will ever be 100% safe). In my case (which is a small, insignificant ios app), I am setting up some observation dashboards in my backend that show me things like "tokens used per user per day", so that I can have an eye on my costs. For example, if a user suddenly doubles or triples their token consumption, something might be fishy.
Additionally, you can try and make it harder for people to actually use "stolen" App IDs, for example by requiring specific data in the requests, so that thieves can't just send the requests from another tool. This can be as simple as adding an identifier in the headers that you occasionally swap (not very safe) or as complex as a generating a new identifier for each request based on some specific hashing algorithm only your app and your backend know (somewhat safer, but apps can always be cracked or decompiled with some effort).
It's mostly a cost vs benefit thing. Nothing can be 100% secure, you have to decide how much work you want to put in that makes it harder for attackers to abuse your backend.
In case of an iOS app, there's also App Attestation, which is part of the DeviceCheck framework, which you can use to verify in the backend that requests are actually coming from your app and that the app has not been tampered with (e.g. on jailbroken devices).
But those are just my thoughts on this topic, I am by no means an expert I might think of all this the wrong way. That’s just my current approach that I’ve decided to go with right now
Hey SwiftedMind, thanks so much for the insights.
My client is explicitly requesting that his API only be accessible to paying customers. The app does not require users to create a separate account, so I don’t have any other auth provider. While it’s unlikely that customers will go to the trouble of inspecting their network traffic, finding their ID and sharing it, I would be lying if I told him that the API was secured. And as new potential RC user I’d like to be confident I’m going about this the right way.
I assume that the RC SDK is already authenticating with the payment account on the user’s device e.g. their Apple ID. Having done so, I had hoped that the RC would provide some token that I could validate on my backend.
Definitely going to look into App Attestation
Hm, yeah, giving any sorts of guarantees is tricky, especially with no account registration (though even then, there’s no guarantee, obviously; account sharing is a real thing). I went down that rabid hole some time ago. Luckily, I’m my own client, so I can decide on the risks I’m willing to take .
(The following passages are my very rough understanding of the mechanics of StoreKit and RevenueCat. I’m not entirely sure it is correct, but maybe there’s some things in it that help you figure out how to solve your problem)
I think the problem is that it’s not possible to get any kind of sensitive information out of the App Store. As far as I know, RevenueCat does not at all know about the user’s Apple ID or account or App Store authentication or anything.
They just get a list of receipts of purchases (or transactions, not sure about the exact terminology) from StoreKit, which is queryable from the user’s device. That is something StoreKit hands out to you, but nothing more. These receipts/transactions will have persistent identifiers that RevenueCat can associate with an App ID (or something along those lines; I hope I’m not entirely wrong about this).
So, your app has to give some kind of information to the backend, which can always be intercepted and re-sent by an attacker through other tools. I don’t think it is possible to have the App Store be the middle entity in this. The app can’t tell the App Store “this account is allowed to use the API, if RevenueCat asks, you can give permission” or something like that.
@sharif
This part from @SwiftedMind
From what I read in the documentation, there might be a way to do this, by sending the users App ID (generated by the RC SDK) to the backend on each request and have the backend call the RC API with that App ID to check the subscription status.
And the part from the documentation
Note that anonymous App User IDs are not able to share subscription status across apps and platforms, so you'll need to identify with a custom App User ID via your own authentication system.
seem contradictory, if I’m not mistaken, because the App ID generated by the RC SDK is indeed anonymous. Therefore, any call to the RC API from the backend using this anonymous App ID is likely to fail or return an incorrect status.
Am I missing something?
Hey @nayem,
I think the key distinction here is that the part of the documentation you’ve linked is talking about multi-platform apps. If you use anonymous App User IDs on two or more platforms (say, iOS and Android apps), there is no way for RevenueCat to match them to the same customer.
However, if you have an iOS-only app, that is not a problem. Even if a user reinstalls the app, or installs it on a second device - and a new anonymous App User ID is generated by the SDK - RevenueCat can simply query StoreKit on the device and fetch all the user’s past transactions (i.e. in-app purchases) because those are tied to the user’s Apple Account. Each transaction has a unique identifier that, I assume, is stored somewhere in RevenueCat’s backend and is assigned to a unique customer. So any newly generated App User ID can also be matched and assigned to that existing customer - through those transactions.
Since I only have an iOS app and don’t plan on adding more platforms, anonymous App User IDs are fine and convenient for me (since I don’t want user accounts in my app). But if you have to support more platforms, you probably have to find another way of doing this, where you create and manage the App User ID yourself in some way (user registration would be the easiest, I guess).