iOS app submission - test url - ios

We have built a iOS application and we are going to submit it to App store. Inside our app we have hardcoded the url of the webservice.
Our client wants to do something like this.
For the review he wants set that url to the webservice in the dev and after approval to change it to production. As he doesn't want to create fake accounts and data in the production database.
Is there any setting or something to do such a thing for the approval without changing our code.
Thanks

Hardcode a specific URL in your app. Then setup your web server so that URL gets redirected to the "real" URL. Before your app is in the store, have the URL redirect to the development URL. Once your app has been approved and it goes live, change the URL to redirect to the production URL.
This is basically a single line change in the web server's config file.
This gets a little trickier if you need to repeat the process each time you submit an update. You probably will want the review of the update to go back to the development URL. This will probably required that you pass a version number as part of the URL (which you should do anyway incase the API of your URL changes over time).

You can check whether on not the app is in AppStore. Check out this answer for more info: https://stackoverflow.com/a/17627239/2604030

I am hoping you haven't also hardcoded a password in there.
However, unless this is a very long-term, robust service, also hardcoding your database server details in there is also a bit dangerous.
Consider writing them to NSUserDefaults but having a failover service.
For example, if the server doesn't respond, have a different server you can hit to get a new database server address.
This would allow you to setup a test server, kill it after review, and have new users go to the new server.
Another approach is, if you have a way to push notifications to the devices or have a status check that they do, add extra metadata to that check message so you can pass through an update to the server address.
Querying a separate server for important metadata is a common technique - it lets you have some flexibility and respond to emergencies.

Why not just KISS and set the URL in NSUserDefaults or a plist (i.e. validation=URL1, validated=URL2 or something). This way your URL is configuration-based and not embedded in your code. You could use Sebyddd's response in your code to determine which URL you grab at runtime

Related

How do I implement deferred deep linking in an iOS app?

I want to implement deferred deep linking in my iOS app as a means of tracking referrals. When a user of my app wants to refer a friend, I'll generate a URL that has a unique referral code. When the other person receives the link and opens it, I want it to take them to my app's page in the App Store. Then if they install my app, when it first opens, I need a way for it to read the referral code from the original URL.
I've found may pages about deferred deep linking on the web but none that really explain how to do it. Instead, these pages all end up telling you to install some third-party code or use some commercial service. This isn't what I'm after. I want to learn how to do this myself.
There are lots of old pages out there that recommend convoluted and error-prone solutions, like tracking the user's IP address, putting the referral code into the clipboard, or somehow obtaining it from a cookie in a web view. I don't think these are the correct solutions to be using in 2022.
If anyone can recommend the appropriate resource, I'd appreciate it.
If it is the case that Apple simply doesn't want us to do this and doesn't provide any support for it, then I'd like to know that too. I was under the impression that they did, but maybe I'm wrong.
Thanks,
Frank
Apple's Universal Links allow for this (would understand the difference between the typical URL Scheme and Universal Links as threshold). This assumes you're willing to do some lifting server-side along with other hurdles on the iOS side, largely administrative.
A benefit of Universal Links and the server-side work is that you're provided a fallback webpage if a user does not have the app installed. Since the app should open if downloaded, you could typically just redirect to the app store from this URL. In this case, though, before any redirects, you could execute an operation to decode the unique params passed in the URL and persist it in a remote data store. The data encoded needs to be required and verifiably unique during your registration -- email seems ideal.
If that's feasible, your standard registration flow could require email verification with a link to the app as a mandatory entry point (think slack magic link). When the user submits his/her email to verify, you could first check that email against your data store to see if it maps to any previously decoded referrals saved from the flow above. If so, you could generate a unique link for this email to your app with params that will direct the deferred/deep link.
The good news is, I found a solution. I could construct a web page that redirects the user to the app store, but before doing so, copies some text into their clipboard (without telling them or asking them to do anything). Then later if they install my app I can get the text by pasting from the clipboard. I tested this idea and it works.
The bad news is, starting with iOS 16, Apple now asks you for permission to paste. So if you try to do this, your user will launch your app and immediately get promoted with a message asking them to allow a paste from Safari. I expect most users will deny the request and just the fact that they saw it will erode their trust in the app (I know I wouldn't trust an app that tried consume my clipboard without a direct command from me).

Why one should not call receipt validation endpoint directly

Apple provides an endpoint to validate receipts:
https://buy.itunes.apple.com/verifyReceipt and warns against calling the endpoint from the app
It is not possible to build a trusted connection between a user’s
device and the App Store directly because you don’t control either end
of that connection, and therefore can be susceptible to a
man-in-the-middle attack.
It is stated, that the safe way is to send the receipt to "my own" server firstly, then communicate with Apple endpoint from the own server.
Honestly, I do not understand how does it help to increase the level of security. Yes, I do not control the /verifyReceipt endpoint, but Apple hopefully does. And why
phone <-> my server <-> Apple server is better than phone <-> Apple server?
Could you elaborate on this from point of view of the hacker (or man-in-the-middle)? How would he tamper the receipt/response in the latter case and what makes it difficult for him in the former case?
It's relatively easy to change an app binary to alter a string within the app. If your binary contains a string "https://buy.itunes.apple.com/verifyReceipt" then an attacker can change the string to "https://example.com/verifyReceipt" (a server that's under their control) and have the server return the receipt JSON that makes it seem like a purchase was successful. This is an easy attack to automate, so you just run a binary through a script which replaces all instances of the Apple URL with another URL.
When you communicate with your own server to Apple you can be (relatively) sure that that connection is secure. There are also ways to secure the communication between the app and your server such as a certificate in the app binary with a public key, the private key on your server. (I'm not an expert at this part so the details about keys might not be 100% correct).
All this being said there are other ways an attacker can make it look like a purchase went through. They can search your app binary for common receipt parsing libraries and flip known booleans (e.g. a userCompletedPurchase boolean flag). These attacks are easily scriptable.
The best way to make sure that your app isn't attacked is to remove the low-hanging fruit and make it that much more difficult that the common scripts won't open your app to attack. One way to do that is to not verify receipts directly with Apple.
I think that the key part to the section from Apple documentation that you cited, but didn't include, was the sentence before.
Using your own server lets you design your app to recognize and trust
only your server, and lets you ensure that your server connects with
the App Store server.
In particular the first half of that sentence (emphasis mine). I see this as a bit of nudge from Apple that the app should be designed as such to "recognize and trust only your server".
In the simplest case (ie. minimal), one could use their server as a proxy to reach Apple's server. The server could simply pass Apple's payload back to the app, or maybe send some simple JSON like
{
success: true
}
Or maybe even it's something as lazy as just sending a 200 response.
Using MITM, it's easy to change the response as needed. So even though you are using your server, the only barrier at this point is the attacker would need to determine what a valid response looks from your server.
They may be able to figure it out by simply sending a bad receipt and then seeing the response. Or maybe a hacker will TOFFT and just make a purchase to get the valid response and then provide others so they can profit.
This is "one step" above going to Apple directly, because everyone knows what the Apple payload is. Thus if the app goes directly to Apple it's easier for someone to be able to run a MITM and replicate a valid receipt response.
Going back to what Apple wrote, they are encouraging you to have a design that is more robust to help mitigate potential fake purchases from occurring. This robustness is what protects you more.
This can take the form in many ways.
For example, perhaps your server sends back the response in an encrypted format. Or replies with a hash. Even better if your app relies on your server through the course of usage, so the server can act as the truth. Or maybe the app periodically "phones home" to get a config of privileges. There are numerous ways one can make it more difficult for a hacker.
Apple is leaving to you just how you determine that trust with your server is established. It is after all, your "money".
As an example, the latest game I've made is specially built to use the server as the truth. So all moves go through the server (game is simulated on the server). While someone could certainly modify the app, in the end the server should theoretically correct any badness introduced.
Note, that it still is possible that someone can bypass even more robust security measures.
You can validate the iOS receipt on the iOS device. But you cannot be sure that the receipt is actually valid. The user could have hacked the device to make you think the receipt is valid.
Your users can edit the executable code of your app, and they can edit the operating system. With common/shared APIs like Apple's purchasing system, there are publicly available tools a user can run on their own phone to avoid paying.
Your server, however, is controlled by you. Your customers do not have physical access to it. And therefore your server (hopefully!) cannot be hacked. This means your server can be trusted, unlike the device. When your server establishes an SSL connection to Apple's server, you know you really are talking to Apple's server. Not one that your user-installed to bypass in-app purchasing.
UPDATE
Let's say you have got your Receipt after a user purchased your consumable product. So now you want to validate it and you have two options:
1. Receipt validation locally(from a user device).
2. Receipt validation through your server.
Local validation:
When you receive the receipt, you then read the contents of the receipt file by passing it through the verifyReceipt endpoint from your app. And you will get a response include a readable JSON body from this endpoints. Based on this response you can validate the receipt and control user action.
And all this happened in your app[user device]. After publishing your app you can't modify your app(if you need) without another release. Also, the user has more control of their device than you.
Validation through your server:
After getting Receipt data you need to encode the data in Base64. Send this Base64-encoded data to your server.
On your server, create a JSON object with the receipt-data, password (if the receipt contains an auto-renewable subscription), and exclude-old-transactions keys detailed in requestBody. Submit this JSON object as the payload of an HTTP POST request. In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt as the URL. In production, use https://buy.itunes.apple.com/verifyReceipt as the URL.
Now you will get a JSON object response from App Store's that contains the keys and values detailed in responseBody. According to the response, you can validate receipt and send a response to the app for further actions.
So, if the user buys something in your app you don't need to store the thing being purchased inside the app. You want to store it on a server, and that server only sends the purchased data to the device after it has verified the receipt with Apple's server.
And you have more control in your server than your app also you can change any mechanism about receipt validation at any time(don't need to update your app) and control your user from the server.
Hope you will get it. Now it depends on you which one you prefer for you.
I will try to put my understanding here as I am deeply involved in auto-renewable subscription workflows.
Why is that endpoint there? what is the purpose?
The purpose of that API is to verify the purchase that user made using your app. So why do you want to call that API from the mobile(phone). In that environment, you already know that user has made the purchase.
But if you want to call that API to decode the encoded receipt inside the app(to read important values), then consider the other recommended approach. That encoded receipt is PKCS7 encoded and Apple itself provide lib to decode that.
Hope this helps

SKProductsRequest over server?

I have an app where I get a list of product IDs from the server, which I then send to Apple to get a list of SKProducts, which I then send back to the server to get html, which I put all together to make a webview with the products.
This is very complicated- is there a way for the server to communicate with Apple directly without having to do all the back and forth?
No. Your app has to make a request to the iTunes store directly from the device it is running on (to ensure correct handling of the user's iTunes account, including appropriate prices for the region, etc.).
It sounds like your app-server communication structure is not the most efficient; it'd be easier if you generated the storefront web view inside your app, locally on the device (or even found a better, native way of displaying the information, such as a UITableView or UICollectionView), instead of sending the IDs to the server. This would eliminate a lot of the back-and-forth resulting from you doing this in a way that was certainly not intended.

Send post data securely from iPhone

I have the following issue:
I've understood how to create a secure login between an iPhone app and a WebServer (SSL,Https).
My question is after creating the session token, how do I make sure that if a hacker intercepts it, in the subsequent POST requests I receive data from the same user?
I ask this because I would have to send the session token each time a request is made right? (to be able to identify the user).
I want to prevent multiple things:
Session hijacking where someone would sniff the users token and send data instead of him (like a highscore or something)
Data injection using data that would not be normally sent from my app like a 1.000.000.000 highscore (possible score but not easily attainable).
I have been looking at:
UDID
User Agent (if it's not from the app name of my app it's not good, the hacker would actually have to guess I do this check or download my php files somehow right?)
The app is from the AppStore. If the request comes from an app that hasn't been approved by Apple it's not ok. I'm not actually sure if you can test this or not. If this works a hacker would have to actually submit an AppStore and download it to insert faulty data into my database which I hope nobody has time for.
The MAC address. Not sure if allowed by Apple. The IP doesn't work because a valid user might change IP's.
Cookies from what I've seen can be easily traced and see what data is inside them.
Maybe I'm not asking the right question here so it could actually be how can I make sure the data I receive is from the correct user and the correct application?
The purpose of SSL around your POST requests is to prevent interception by a third-party in transit. If a hacker can get to it, it means the token was either leaked on the client (rooted device), server (insecure application logging/debugging) or they broke SSL. (unlikely)
You could perform some advanced checking by capturing device UDID (apple doesn't like this) or comparing to source IP, but it is going to be a lot of effort for questionable security improvement.
Just ensure everything sensitive is in SSL and you should be ok.

Prevent installing passbook on multiple devices

Not exactly a programming question but here it goes:
How can a company who is distributing passbook passes via email or web prevent a pass from being installed on more than one device?
I can not find anything about this on Apple docs. The only I can think of is to check on the device registration webservice whether the combination of pass type and serial has a device already registered and delete it , but I am not aware of any command to delete the pass remotely.
Another option would be to check if it is already registered prior to generating the pass but this would only work for URL distribution, not for email.
Is there any way to delete a pass remotely via push notification + update? Any ideas on how to solve this issue?
Mail and Mobile Safari will present any pass they are given and the user can decide to add them to their Passbook. There is nothing the pass creator can do to prevent it except to be careful about how the .pkpass files get handed around.
If you really only want to deliver a specific pass to a specific device you might consider a companion app that uses a custom API to communicate with the the backend and request the pass for that device that way. Then you have much more control than distribution via email or url links.
Apple frowns on trying to delete a pass programmatically; only users are supposed to delete passes because they added them. You can, however, update a pass to make it clear that is not valid and should be deleted. For example you can remove the bar code, if any, and use a background image with a big red "INVALID" on it.
Just to extend #ohmi's answer:
You cannot prevent passes from being installed on more than one
device - e.g. if user enables iCloud for Passbook, the passes will get
synced automatically across devices.
Considering your links to pkpasses are public, you may want to consider
introducing one-time download links, but while it can fill your
needs just fine, users can be really disappointed if it's impossible to re-add
passes that they manually deleted. So I wouldn't recommend such solution.
You can make you pkpass links kind of private, so only GET request originating from your application and carrying a specific value for specific header field (e.g. auth_token), will receive a pkpass file, however this way you almost disable pass distribution via email or via sharing URLs to passes and make pass updating probably impossible.

Resources