apache / cordova-ios

Apache Cordova iOS
https://cordova.apache.org/
Apache License 2.0
2.16k stars 988 forks source link

6.1.0 and signalR not working #944

Closed barakataboujreich closed 1 year ago

barakataboujreich commented 4 years ago

Description

hello guys, we are trying to upgrade cordova-ios to 6.1.0 since uiwebview is deprecated and wont be accepted on stores anymore. we had everything working on uiwebview. i managed to make XMLHttpRequest work with null origin, and we are able to make api calls again. but we are facing a problem with signalR, it seems that it's not able to connect. i am not able to figure out the problem.

What is expected to happen?

signalR to connect normally to the server.

What does actually happen?

signal R isn't able to connect.

Information

for a start i got this call:

Summary
URL: http://192.168.47.105:55554/signalr/connect?transport=serverSentEvents&clientProtocol=1.5&connectionToken=OC0LMC6PzDP%2BbksoG4PF3OlmAX6ScrTdg3yOxtaZFnHAspQjvqfFpDSCIWJGLNME%2FlCUg86Vpg1h12VOoR2uB0b2LyBVPbsRU%2BVo%2BNONkFIgdFfNcVahRec6xURdjwMs&connectionData=%5B%7B%22name%22%3A%22kindoohub%22%7D%5D&tid=9
Status: 200 OK
Source: Network

Request
Accept: text/event-stream
Cache-Control: no-cache
Origin: null
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
Pragma: no-cache

Response
Content-Type: text/event-stream
Access-Control-Allow-Origin: null
Pragma: no-cache
Access-Control-Allow-Methods: GET, POST
Expires: -1
Transfer-Encoding: Identity
Access-Control-Allow-Headers: Content-Type
Cache-Control: no-cache
Date: Wed, 15 Jul 2020 12:37:22 GMT
Access-Control-Allow-Credentials: true
X-Content-Type-Options: nosniff
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Server: Microsoft-IIS/7.5

Query String Parameters
transport: serverSentEvents
clientProtocol: 1.5
connectionToken: OC0LMC6PzDP+bksoG4PF3OlmAX6ScrTdg3yOxtaZFnHAspQjvqfFpDSCIWJGLNME/lCUg86Vpg1h12VOoR2uB0b2LyBVPbsRU+Vo+NONkFIgdFfNcVahRec6xURdjwMs
connectionData: [{"name":"kindoohub"}]
tid: 9

although the status is 200 ok, it is marked in red, and the preview shows an error occured trying to load the resource. then i get this in console: XMLHttpRequest cannot load http://192.168.47.105:55554/signalr/abort?transport=serverSentEvents&clientProtocol=1.5&connectionToken=OC0LMC6PzDP%2BbksoG4PF3OlmAX6ScrTdg3yOxtaZFnHAspQjvqfFpDSCIWJGLNME%2FlCUg86Vpg1h12VOoR2uB0b2LyBVPbsRU%2BVo%2BNONkFIgdFfNcVahRec6xURdjwMs&connectionData=%5B%7B%22name%22%3A%22kindoohub%22%7D%5D.

now this is my config.xml:

<?xml version='1.0' encoding='utf-8'?>
<widget id="{{myappid}}" version="3.6.3" xmlns="http://www.w3.org/ns/widgets" xmlns:gap="http://phonegap.com/ns/1.0">
    <name>kindoo</name>
    <description>
        kindoo is an application that transforms any electric lock to a smart lock
    </description>
    <author email="support@codegears.com" href="http://codegears.com">
        Code gears
    </author>
    <content src="index.html" />
    <preference name="permissions" value="none" />
    <preference name="orientation" value="default" />
    <preference name="target-device" value="universal" />
    <preference name="fullscreen" value="true" />
    <preference name="webviewbounce" value="true" />
    <preference name="prerendered-icon" value="true" />
    <preference name="stay-in-webview" value="false" />
    <preference name="ios-statusbarstyle" value="black-opaque" />
    <preference name="detect-data-types" value="true" />
    <preference name="exit-on-suspend" value="false" />
    <preference name="ShowSplashScreenSpinner" value="false" />
    <preference name="AutoHideSplashScreen" value="false" />
    <preference name="disable-cursor" value="false" />
    <preference name="android-minSdkVersion" value="14" />
    <preference name="android-installLocation" value="auto" />
    <preference name="DisallowOverscroll" value="true" />
    <preference name="StatusBarOverlaysWebView" value="false" />
    <preference name="StatusBarBackgroundColor" value="#142515" />
    <preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" /> 

    <gap:plugin name="org.apache.cordova.battery-status" />
    <gap:plugin name="org.apache.cordova.camera" />
    <gap:plugin name="org.apache.cordova.media-capture" />
    <gap:plugin name="org.apache.cordova.console" />
    <gap:plugin name="org.apache.cordova.contacts" />
    <gap:plugin name="org.apache.cordova.device" />
    <gap:plugin name="org.apache.cordova.device-motion" />
    <gap:plugin name="org.apache.cordova.device-orientation" />
    <gap:plugin name="org.apache.cordova.dialogs" />
    <gap:plugin name="org.apache.cordova.file" />
    <gap:plugin name="org.apache.cordova.file-transfer" />
    <gap:plugin name="org.apache.cordova.geolocation" />
    <gap:plugin name="org.apache.cordova.globalization" />
    <gap:plugin name="org.apache.cordova.inappbrowser" />
    <gap:plugin name="org.apache.cordova.media" />
    <gap:plugin name="org.apache.cordova.network-information" />
    <gap:plugin name="org.apache.cordova.splashscreen" />
    <gap:plugin name="org.apache.cordova.vibration" />
    <platform name="ios">

        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
        <allow-navigation href="http://192.168.47.105:55554/*" />
        <allow-navigation href="http://www.{{mydomainname}}.tech/*" />
        <allow-navigation href="http://*.{{mydomainname}}.tech/*" />
        <allow-navigation href="https://www.{{mydomainname}}.tech/*" />
        <allow-navigation href="https://*.{{mydomainname}}.tech/*" />
        <icon height="20" src="res/icon/ios/icon-20.png" width="20" />
        <icon height="40" src="res/icon/ios/icon-20@2x.png" width="40" />
        <icon height="60" src="res/icon/ios/icon-20@3x.png" width="60" />
        <icon height="29" src="res/icon/ios/icon-29.png" width="29" />
        <icon height="58" src="res/icon/ios/icon-29@2x.png" width="58" />
        <icon height="87" src="res/icon/ios/icon-29@3x.png" width="87" />
        <icon height="40" src="res/icon/ios/icon-40.png" width="40" />
        <icon height="80" src="res/icon/ios/icon-40@2x.png" width="80" />
        <icon height="120" src="res/icon/ios/icon-40@3x.png" width="120" />
        <icon height="50" src="res/icon/ios/icon-50.png" width="50" />
        <icon height="100" src="res/icon/ios/icon-50@2x.png" width="100" />
        <icon height="57" src="res/icon/ios/icon-57.png" width="57" />
        <icon height="114" src="res/icon/ios/icon-57@2x.png" width="114" />
        <icon height="120" src="res/icon/ios/icon-60@2x.png" width="120" />
        <icon height="180" src="res/icon/ios/icon-60@3x.png" width="180" />
        <icon height="72" src="res/icon/ios/icon-72.png" width="72" />
        <icon height="144" src="res/icon/ios/icon-72@2x.png" width="144" />
        <icon height="76" src="res/icon/ios/icon-76.png" width="76" />
        <icon height="152" src="res/icon/ios/icon-76@2x.png" width="152" />
        <icon height="167" src="res/icon/ios/icon-83@2x.png" width="167" />
        <icon height="1024" src="res/icon/ios/icon-1024.jpg" width="1024" />
        <splash src="res/screen/ios/Default@2x~iphone~anyany.png" />
        <splash src="res/screen/ios/Default@2x~iphone~comany.png" />
        <splash src="res/screen/ios/Default@2x~iphone~comcom.png" />
        <splash src="res/screen/ios/Default@3x~iphone~anyany.png" />
        <splash src="res/screen/ios/Default@3x~iphone~anycom.png" />
        <splash src="res/screen/ios/Default@3x~iphone~comany.png" />
        <splash src="res/screen/ios/Default@2x~ipad~anyany.png" />
        <splash src="res/screen/ios/Default@2x~ipad~comany.png" />

        <edit-config file="*-Info.plist" mode="merge" target="NSBluetoothPeripheralUsageDescription">
            <string>Bluetooth is needed to communicate with KINs</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSBluetoothAlwaysUsageDescription">
            <string>Bluetooth is needed to communicate with KINs</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
            <string>Camera is needed to take profile picture</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSMicrophoneUsageDescription">
            <string>Microphone is needed to take a video</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSPhotoLibraryUsageDescription">
            <string>Library is needed to upload profile picture</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSLocationAlwaysUsageDescription">
            <string>Location is needed to communicate with KINs</string>
        </edit-config>
        <edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
            <string>Location is needed to communicate with KINs</string>
        </edit-config>
    </platform>
    <access origin='*' allows-arbitrary-loads-for-media='true' allows-arbitrary-loads-in-web-content='true' allows-local-networking='true' />
    <allow-navigation href="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <feature name="CDVWKWebViewEngine">
    <param name="ios-package" value="CDVWKWebViewEngine" />
    <preference name="scheme" value="http" />
    <preference name="hostname" value="192.168.47.105" />
</feature>
</widget>

i have these customheaders in my server Web.config

<customHeaders>
        <add name="Access-Control-Allow-Headers" value="Content-Type" />
        <add name="Access-Control-Allow-Methods" value="GET, POST" />
        <add name="Access-Control-Allow-Credentials" value="true" />
</customHeaders>

and i have this code in Global.asax.cs to handle the null origin:

protected void Application_BeginRequest(object sender, EventArgs e)
        {
            if (Context.Request.UrlReferrer != null)
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", Context.Request.UrlReferrer.GetLeftPart(UriPartial.Authority)); //browser
            else
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "null"); //cordova
        }

and for my startup:

public class Startup
    {

        public void Configuration(IAppBuilder app)
        {
            try
            {
                string redisIP = ConfigurationManager.AppSettings["RedisIP"].ToString();
                int redisPort = int.Parse(ConfigurationManager.AppSettings["RedisPort"].ToString());
                GlobalHost.DependencyResolver.UseRedis(redisIP, redisPort, null, "SignalRChat");

                app.Map("/signalr", map =>
                {

                    //map.UseCors(CorsOptions.AllowAll);
                    var hubConfiguration = new HubConfiguration
                    {
                        EnableDetailedErrors = true,
                        EnableJavaScriptProxies = true,
                    };

                    map.RunSignalR(hubConfiguration);
                });
            }
            catch (Exception ex)
            {
                File.WriteAllText(@"C:\Logs\ex" + DateTime.UtcNow.Ticks.ToString() + ".txt", ex.Message + " " + ex.StackTrace);
            }
        }

    }

i tried enabling signalr cors but with no use and different error thrown about allow credentials to be different than true. i cant find the error or what is causing this problem if anyone can help me.

Environment, Platform, Device

Backend: .Net 4.5 webapi cordova: 9.0.0 cordova-ios: 6.1.0 device: iphonex device ios: 13.5.1 SignalR: 2.2.1.0

Checklist

breautek commented 4 years ago

I'm not really sure what is causing it not to work but one thing I noticed is that it doesn't appear that you accept/respond to the OPTIONS preflight request.

When it comes to CORS, some requests will trigger a preflight request.

In my nginx config I basically do:

if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' $http_origin always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, OPTIONS, HEAD' always;
    add_header 'Access-Control-Allow-Headers' 'Accept, Content-Type, Access-Control-Allow-Origin' always;
    add_header 'Content-Type' 'text/plain charset=UTF-8' always;
    add_header 'Content-Length' 0 always;
    return 204;
}

I'm sure you can adapt that to IIS equivilant. $http_origin is a variable for the incoming request's Origin header value.

Not sure if this will solve the issue with your event stream request, but I think it's worth a try.

barakataboujreich commented 4 years ago

sorry for my late reply, i tried what you suggested, didn't work. but with further debugging and analyzing we discovered that signalr is being connected, but the cookies aren't being sent, so the server is refusing the connection.

our authentication api is successfully returning a cookie to the client. but any other request that follows is not able to send a cookie. do you know a solution for this ?

breautek commented 4 years ago

Ah, cookies is a common issue with wkwebview, ever since they implemented some kind of caching mechanism or something. I don't really know all the details.

You could try reaching out to or cordova-ios channel on our slack on the cookie issue.

barakataboujreich commented 4 years ago

well for now i used this plugin to sort out our cookies problem, cordova-plugin-wkwebview-inject-cookie . i can work with only IOS 11 for now, better than nothing, till we get a proper solution.

breautek commented 4 years ago

can work with only IOS 11 for now, better than nothing

cordova-ios@6 only supports iOS 11+ anyway.

barakataboujreich commented 4 years ago

i guess this plugin documentation needs clarification or update, as they stated it works only on IOS 11. I tested it on IOS 13.5.1 and it works as it's supposed to. so maybe it could be a solution or walkaround for cookies in cordova-ios@6

breautek commented 4 years ago

I'll reopen this issue, I'm sure something can be done and we might be able and while we can't just use that plugin's codebase, we can draw inspiration from it.

That plugin requires the developer to add:

document.addEventListener('deviceready', () => {
  wkWebView.injectCookie('mydomain.com/mypath');
});

I wonder if there is a way to have this done automatically.

Unfortunately I don't have mac hardware to experiment or develop the feature, but I do think it is something worth investigating.

iwan-uschka commented 4 years ago

while we can't just use that plugin's codebase, we can draw inspiration from it

Checking out this plugin it turned out not to be working correctly on iOS 13 (https://github.com/CWBudde/cordova-plugin-wkwebview-inject-cookie/issues/7#issuecomment-627413175). For the record...

barakataboujreich commented 4 years ago

i'm sorry, i do not know what is not working on your end. my testing environment is: Iphone X IOS 13.5.1

Back end: .Net 4.5

my problem was: cookies not being received on the server, which caused the back end to refuse the signal R connection.

solution that worked for me was this plugin.

how i solved it: on deviceready event

i added the following:

wkWebView.injectCookie("http://192.168.47.105:55554/WebService.asmx");
wkWebView.injectCookie("http://192.168.47.105:55554/signalr");

and config.xml:

<preference name="scheme" value="http" />
<preference name="hostname" value="192.168.47.105" />

after that our server was able to access cookies and signal R connection was successful. and from safari, went to the network tab and monitored every xhrhttprequest and was sending cookies to the server, and server was able to access those cookies.

barakataboujreich commented 4 years ago

i do not know what this plugin is doing to be honest, i have zero experience in objective-c. but document.cookie return empty string

but as the following photo login call returning a response cookie

and for the following two requests, the response cookie is being sent request 1 request 2

gill367 commented 4 years ago
<preference name="scheme" value="http" />
<preference name="hostname" value="192.168.47.105" />

How does the scheme 'http' work for you. When I put it in config.xml. it uses the default app scheme instead.

breautek commented 4 years ago
<preference name="scheme" value="http" />
<preference name="hostname" value="192.168.47.105" />

How does the scheme 'http' work for you. When I put it in config.xml. it uses the default app scheme instead.

http is a reserved scheme on iOS. Which is why it defaults to the app:// scheme, the cordova default.

gill367 commented 4 years ago
<preference name="scheme" value="http" />
<preference name="hostname" value="192.168.47.105" />

How does the scheme 'http' work for you. When I put it in config.xml. it uses the default app scheme instead.

http is a reserved scheme on iOS. Which is why it defaults to the app:// scheme, the cordova default.

and If I use a custom scheme like app:// dummy cookie injection does not work for me. So all subsequent xhr request using session cookies are receiving 401. Anybody is able to use cookie injection with app custom scheme?

dpogue commented 1 year ago

I've marked this issue as stale because it's been over a year with no further comments. If this is still an issue in the latest cordova-ios version and an up-to-date iOS version, please let us know. Otherwise, this issue will be closed.

dpogue commented 1 year ago

Closing as stale.