tl;dr
Essentially i need to set COOP/COEP headers to enable SharedArrayBuffer functionality in my Capacitor v3 iOS App and running a ServiceWorker to set the headers is the only solution i have found so far.
Background to the problem
Both Chrome Android and Safari iOS now support SharedArrayBuffer
which now means Ffmpeg.wasm can utilise multi-threading. SharedArrayBuffer requires a secure context to enable functionality, this is done by setting the following headers;
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Further requirements;
The site needs to be served over HTTPS or localhost
The headers have to be set server side (headers cannot be set using meta http-equiv=".." in the head tag)
I've found this problematic in Capacitor to implement. My App is self contained (App files served locally), it doesn't require external http requests to serve the App files. This means setting the required headers server-side isn't an option.
The Work-around solution
The following solution suggests using a ServiceWorker to set the COOP/COEP headers client-side, which is exactly what i need. Enabling COOP/COEP without touching the server
The Problem
ServiceWorkers have their own set of requirements and this is where i'm stuck.
To enable this ServiceWorker API on iOS i'm using App-Bound Domains.
The following issue in github Support deploying a PWA with capacitor as native app outlines the steps required to implement App-Bound Domains in Capacitor.
Which include;
Adding a list of your domains incl. server.hostname / localhost to Info.plist.
Enabling limitsNavigationsToAppBoundDomains in your src - capacitor/capacitor.config.json
After setting App-Bound Domains i can the following error;
serviceWorker.register() must be called with a script URL whose protocol is either HTTP or HTTPS
This happens because Capacitor apps are served from capacitor:// in iOS.
Is there anyway around this? ( that does not require serving from a remote url).
Or has anyone discovered a better approach to setting COOP/COEP headers client-side in Capacitor?
Thanks Cam
Related
I am using Angular Capacitor v3 with axios. Receiving a Network Error when making any request to any external server. This is only occurring while emulating with XCode. The request never reaches the server, just returns immediately with status 0.
I cannot repeat this problem locally on Windows, or published website, or Android Studio emulators, or published to android device.
Not sure if I am missing a permission or configuration, but I have tried adding "Local Network Usage", "Location Always and When In Use" permissions and played around with NSAppTransportSecurity settings to the Info.plist.
I do not believe its an issue with the server since it seems like it never even reaches it. But I have a verified HTTPS certificate, hosted by Azure, with valid CORS rules.
I have also tried HttpClient with Angular, same result.
The error occurs immediately and does not give specific information but here is the message:
{"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown
Error","url":"https://jsonplaceholder.typicode.com/users/1","ok":false,"name":"HttpErrorResponse","message":"Http
failure response for https://jsonplaceholder.typicode.com/users/1: 0
Unknown Error","error":{"isTrusted":true}}
After extensive research and rebuilding from scratch, I found this was a CORS related issue but specific to setup with Azure, which was my hosted server. The confusing part was that even external example calls, such as to "http://jsonplaceholder.typicode.com/users/1" still gave the same error, which wouldn't have CORS issues, as far as I understand. If anyone knows why that happens, please let me know.
This was related to the fact that in ios, the http/https schemes cannot be used, instead it uses capacitor. This scheme is not allowed in Azure App Service CORS settings, unless you allow all, but I could not because I needed to enable Access-Control-Allow-Credentials. So I had to build the CORS configuration with the capacitor scheme allowed in my C# app service code. Removing all settings from CORS in Azure App Service then allowed the settings in my C# code to be applied.
I am just facing same issue
It was working on different domain, no hostname is different and it stopped working.
So it doesn't even make a request to a server.
Also same configuration works on android. just not IOS
We have an Ionic 4 (Cordova/Angular) app that makes requests to a REST API backend with HttpClient, using cookies for authentication.
The app runs fine on Android (which apparently proves that we've configured CORS correctly). We are now in the process of adapting it for iOS. We have found that when the Safari setting "prevent cross-site tracking" is turned on (which it is by default), WkWebView ignores the Set-Cookie header that is sent by the backend in response to a successful login request, causing all subsequent requests to fail.
Is there a way to avoid the problem without requiring all users to turn off this setting (which is definitely not an option)? We have considered switching to JWT authentication, but I'm afraid that it won't solve the problem, as our backend expects JSON, and according to the docs, "Content-Type: application/json" headers also cause the request to be considered as a CORS request.
Additionaly, we have come across another problem which seems to be related. The app won't run on the iOS simulator on macOS - the Set-Cookie headers are ignored, even if we switch the above setting off! Meaning we can debug the app only on a physical device, which is quite a hindrance.
After several hours of searching on the web, and not finding any similar complaints, I have the feeling that I'm missing something obvious.
This library has come to my rescue: ionic-native-http-connection-backend
Quoting from the docs:
This library adds #ionic-native/http (when available) as a connection backend to Angular's Http and HttpClient
Motivation
Now that Apple promotes/requires the use of WKWebView instead of the deprecated UIWebView, Ionic has switched newly created apps over via their cordova-plugin-ionic-webview (and Cordova offers it via their cordova-plugin-wkwebview-engine). That causes requests that used to work just fine to fail with CORS errors.
The real solution of course is to fix the CORS issues server side - but this may not be possible with e.g. 3rd party APIs.
Even though there is a way to solve CORS issues without changing server's response header by using #ionic-native/http, this only works on device and doesn't provide all the power of Angular's Http and HttpClient.
How it works
The library provides a HttpBackend interface for Angular's HttpClient
This HttpBackend interface tries to use #ionic-native/http whenever it is possible (= on device with installed plugin)
If HttpBackend finds it impossible to use #ionic-native/http, it falls back to standard Angular code (HttpXhrBackend, which uses XmlHttpRequest)
This strategy allows developers to use Angular's HttpClient transparently in both environments: Browser and Device.
Although I still find it strange, that the developer only mentions the library's intended use for situations where CORS can't be configured server-side, but not the 2 problems that it solves for me, namely 1) having to require all users to allow cross-site tracking, and 2) not being able to use the simulator.
I am using Cordova to build an iOS App. I use the Ionic engine meaning that the app is served from a webserver running on the device.
My window.location.origin is "ionic://my-app.com".
A third party API (that I need to use in order to fetch an address suggestion based on what the user types) rejects my HTTP requests because the referrer header is missing in the request.
It's not possible to programmatically add a referrer from the JS layer and these did not work:
<meta name="referrer" content="origin">
<meta name="referrer" content="always">
I suspect that the issue is that the API is using https and my iOS App is using a custom scheme (the default one is ionic:// and cannot be changed to https). For this reason the referrer header is not added by the WebView.
How can I solve this issue?
I'd try following the CORS workaround for native apps that uses the HTTP plugin from ionic. See here for more details:
https://ionicframework.com/docs/faq/cors#1-native-only-apps-ios-android-
if you are using the webview package cordova-plugin-inappbrowser you can integrate this Pull Request in your code and you will be able to set a custom header with a custom value: https://github.com/apache/cordova-plugin-inappbrowser/pull/363
I have done it and it works.
I am writing an iOS app that hits one of our own web servers to leverage data. The IIS web server is publicly-accessible, has valid cryptography certificates, and uses TLS 1.2. To my knowledge, all that is up to snuff with App Transport Security. When making web requests in the app, the request times out, but more interestingly, this message is logged "NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9806)".
This IIS server happens to Accept X509 client certificates for a separate use case that does not involve the mobile app. Changing the setting to Ignore results in the iOS app hitting the server fine. I assume the timeout occurs because the server is prompting the app for a client certificate and it doesn't just respond that it doesn't have one. Note, I don't have the client certificates set to Require. I am not sure how to have the iOS app play nicely and just carry on when prompted by the server for a client certificate.
Is there a way to get this to work in iOS while allowing the IIS server to still Accept client certificates? I don't want to diminish ATS by adding hacky exclusions to info.plist.
I don't think it's relevant, but I am developing the iOS app in Xamarin.IOS in C#. A request goes something like this:
using (var client = new WebClient())
{
client.Headers.Add(Constants.RequestHeaders.RequestId, nonce.RequestId.ToString());
client.Headers.Add(Constants.RequestHeaders.Signature, Convert.ToBase64String(nonce.DigitalSignature));
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var resultJson = client.UploadString("https://foo.com/Api/Register", json);
}
EDIT:
I was able to resolve this issue by using the library ModernHttpClient available via NuGet. The API site accepts client certificates, but does not require them. In most browsers, this results in a one-time prompt by the browser asking you to specify the cert you'd like to use. However, in iOS, making a programmatic web request by default does not bring up a prompt (of course) nor does it inform the server it does not have a cert to provide. Hence, the request simply timed out. With ModernHttpClient, I found a way to set this behavior to automatically resolve.
var handler = new NativeMessageHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
using (var client = new HttpClient(handler))
{ ...
Now it works fine. I'd prefer to not have to include a library just for this very specific purpose, but it works. Perhaps this specific functionality could be distilled into a few lines of code without using the library? Regardless, I'm thankful for ModernHttpClient solving this issue; it's really hacky to change server behavior just to support an iOS quirk.
Can you try change iOS Build setting.
change SSL/TLS implement use * Apple TLS *
change HttpClient implementation use * NSUrlSession *
I had similar problem and using HttpClient to call our public Https API, above setting fixed the issue.
Trigger.io has just updated today and we found the new UI great! But when we run our Application to android Emulator, we suddenly found this issue:
[ERROR] XMLHttpRequest cannot load http://site.com/api/. Origin content://com.sample.android.app is not allowed by Access-Control-Allow-Origin. -- From line 1 of null
[ERROR] Uncaught SyntaxError: Unexpected token o -- From line 1 of
We are trying to access the api and then an error occured. It is working fine in Iphone but having a trouble in Android.
I hope trigger.io can address this issue please.
Trigger apps on Android are served via content:// urls which means they are not permitted to make requests to other domains, on iOS file:// urls are used so this restriction is not in place.
You have 2 options to fix this, either set the Access-Control-Allow-Origin header sent by the server to *, or use forge.request.ajax to make the request. forge.request.ajax uses native code to make the request and so avoids any Javascript security limitations, more documentation is available here: https://trigger.io/docs/current/api/modules/request.html