SFSafariViewController will sometimes ignore redirects to non-https URLs if no user interaction occurs

We have a payment process in an app which involves loading up a Web page to allow a user to enter credit card details, and to complete a transaction. This web page may result in a 3D-Secure step up challenge. For PCI compliance purposes we launch this in a SFSafariViewController.

This 3D secure process involves a collection of redirects, with a final one back to us known as the "Merchant Page". In here, we want to do one more redirect, using a custom URL scheme, to allow the app to recognise that the process has completed, and can safely dismiss the SFSafariViewController.

In those cases where no "step up 3d challenge" occurs, the process works well, the final redirect occurs, and our app successfully dismisses the SFSafariViewController.
However, in a step up challenge, the Merchant Page loads, but any attempt to do the final custom redirect does not execute.

I believe this is a feature of Safari - What I think is going on is that enough time has passed since there was user interaction on the page, and the final redirect, which isn't a https or http link (it is a myapp:// link) simply gets ignored.

We've tried a lot of things. One thing works: If we provide a button, and when it is tapped, it sets window.location.href = 'myapp://success?<ourdata>', this works.

This is a clue to if it is cause by lack of user interaction.

We've tried lots of things:

  1. Rather than a javascript redirect, we tried server side redirecting; we tried all of the HTTP 300 response codes. They were all ignored
  2. We tried the meta refresh (which we know is kinda deprecated).
  3. We tried window.location.href = , window.location.assign(), document.location.href, document.location.assig(), etc.
  4. We tried issuing an Ajax XMLHttpRequest, but this failed because it isn't https or http
  5. We even tried to suggest our non-standard URL was the source of a script, just to trigger it but it didn't work.
  6. We've tried calling in during body onload
  7. We've tried calling it using a timeout in case timing was relevant.

My gut feeling here is this is a feature. I've used Safari Debugging, and it literally steps over the window.location.href assignment, and doesn't produce a warning or an error. We've added try/catch, and no exception was thrown. Again, it leads me to believe this is all by design (perhaps to prevent ad fraud or something?).

I was kinda hoping that in the response, we'd be able to specify a CORS header that tells the browser that "it will be ok to use resources from myapp://", but haven't found the right one.

We may end up having to simply produce a button with a message "Your transaction has completed, please press here to dismiss", but it is terrible UX that is unnecessary.

I've seen a number of posts elsewhere suggesting that redirects without user interaction can be considered suspicious, and I've experience of this same problem on another browser.

If anyone has cracked this one, I'd love to know how

Replies

I tried another approach, which was defining my protocol in my content security policy header: Content-Security-Policy: connect-src myapp:; script-src 'unsafe-inline' And then calling fetch() on "myapp://<rest of the url>". Safari shows an error on this: Refused to connect to "myapp://rest of the url>" because it does not appear in the connect-src directive of the Content-Security-Policy I've tried Content-Security-Policy: connect-src myapp:; script-src 'unsafe-inline' Content-Security-Policy: connect-src myapp:; script-src 'unsafe-inline' Content-Security-Policy: connect-src myapp://; script-src 'unsafe-inline' Content-Security-Policy: connect-src myapp://*; script-src 'unsafe-inline'

None of these work, but do produce different errors.