31

Bear with me, this one needs a bit of explanation.

I am helping to build a hybrid mobile web app. The main codebase is HTML5 and JavaScript, which will be wrapped in a native mobile Web View (a la Phonegap).

Part of the functionality requires the app to post information to a web service controlled by one of our clients. There is very little scope to change this web service as it is being used by others. We send JSON using an HTTP POST and receive responses from the server. Part of this response is a JSESSIONID cookie which manages our session with the server. After the initial initSession() call, we need to send the JSESSIONID cookie with every (AJAX) request.

When deployed on a mobile device, the web app is wrapped in the native Web View, which starts the web app by browsing to file:///path/to/app/index.html.

The first thing we tried was asking our client to set Access-Control-Allow-Origin: * in their response header to allow CORS. We then tried posting to the server:

$.ajax({
  url: 'http://thirdparty.com/ws',
  data: data,
  type: "POST",
  dataType: "JSON",
  success: successCallback,
  error: failedCallback
});

Monitoring the requests, it was apparent that the cookies were not being included. On closer inspection there is a special section in the CORS spec for dealing with user credentials, which includes session cookies. So I modified the AJAX call to include this:

$.ajax({
  url: 'http://thirdparty.com/ws',
  data: data,
  type: "POST",
  dataType: "JSON",
  success: successCallback,
  error: failedCallback,
  xhrFields { withCredentials: true }
});

Another error, this time from the Browser. More reading yielded the following:

If the third party server did not respond with an Access-Control-Allow-Credentials: true header the response would be ignored and not made available to web content.

Important note: when responding to a credentialed request, the server must specify a domain in the Access-Control-Allow-Origin header, and cannot use wild carding.

So we need to change the server's headers to include Access-Control-Allow-Credentials: true and Access-Control-Allow-Origin to our Origin.

Here we finally come to my problem: when loading a web page using the file:// protocol, the Origin request header sent from the Web View is set to null. It therefore can't be parsed by the server and so the server can't set it in Access-Control-Allow-Origin. But if the server can't set Access-Control-Allow-Origin to something other than * we can't send credentials, including cookies.

So I'm stuck. What to do? I saw a similar question posted here but I don't really understand the proposed answer. Any help would be much appreciated!

Community
  • 1
  • 1
Tom Spencer
  • 7,816
  • 4
  • 54
  • 50
  • 3
    The only solution I can think of would be to use a server to hit the web service. You can write an HTTP handler and hit that from your mobile app. Obviously this adds additional overhead in server maintenance but you must have something available, no? – Alex Morales Feb 02 '12 at 03:13
  • Yes, that is the interim solution, a nasty php curl intermediate gateway. It's really not ideal, but does at least work for the time being. – Tom Spencer Feb 02 '12 at 18:18
  • One thing I might do is ask the phonegap devs if they've seen anything like this before and have any workarounds... – Tom Spencer Feb 02 '12 at 22:53
  • The way we solved this is we used nginx as a reverse proxy on the server side to redirect to the third party authentication system and get the response back. If you are using apache, you can do the same. Or you can use any of the (other ways to circumvent Same origin policy)[http://stackoverflow.com/questions/3076414/ways-to-circumvent-the-same-origin-policy]. – Pradeep Mahdevu Oct 04 '13 at 14:50
  • @AlexMorales I don't understand how or why using this server would solve the problem with file://. Can you explain the process in detail? Thanks. – savram Jun 20 '18 at 18:25

6 Answers6

7

I realize this question is old, but I figured I'd throw in on it anyhow. In the case of CORS requests, the browser preflights them. What this means is - in spite of whatever $.ajax() method you are using, an OPTIONS request is sent to the server.

What this preflighted OPTIONS request is actually doing is saying:

"Hey there, foreign-server-from-some-other-domain, I want to send you a not-simple request (simple req's are not preflighted). My not-simple request going to have these kinds of headers and content type and so on. Can you let me know if this is okay?"

Then the server will do whatever it does (probably check some configuration or database) and respond with the allowable origin(s), the allowable header(s), and/or the allowable method(s).

Finally - if that preflight OPTIONS request has received response that allows the actual $.ajax() method to go - it goes.

CORS is not the same as JSONP.

All that said - while withCredentials preflight success requires the response to carry a Access-Control-Allow-Credentials header (as stated in the question), that is IN ADDITION to Access-Control-Allow-Origins AND Access-Control-Allow-Methods values, which must include the facets of the intended request.

For example - if you are making a CORS POST request from origin http://foo-domain.com with headers somevalue to http://bar-domain.com, a preflight OPTIONS request would go out and in order for the actual post request to be made to http://bar-domain.com, the OPTIONS request would need to receive a response with an Access-Control-Allow-Origins value that included http://foo-domain.com. This could be the origin name itself or *. The response would also need to have an Access-Control-Allow-Methods value that included POST. This may also be *. And Finally if we want our somevalue header to be allowed, the response must contain a Access-Control-Allow-Headers value that includes our somevalue header key or *.

To circle back - if you can't control the server, or have no way to allow the server to allow your CORS requests, you could always use JSONP or some urlEncoded datatype and/or make simple requests without custom headers. GET, HEAD, and full POST requests are usually simple requests.

J.Wells
  • 1,749
  • 12
  • 13
  • 2
    Side note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding (`*`). – T J Nov 26 '15 at 10:03
2

I imagine that if you are creating a hybrid application you are using cordova. If that is the case you don't need CORS you just need to white list the domains you are going to access.

http://docs.phonegap.com/en/3.0.0/guide_appdev_whitelist_index.md.html

1

My suggestion is set ACCESS-CONTROL-ALLOW-ORIGIN to null on server side

Yes, This question bothers me for a little bit.

Regarding to CORS spec, null can cater the situation where a CORS request from a file:// scheme

And a pratical recommendation on that spec is to set it as origin-list-or-null, which is either a list of space-separated origins or simply "null" (by the way, the string %x6E %x75 %x6C %x6C from the definition for origin-list-or-null is literally null hex- encoded)

Finally you will ask, wont that equal to * if we set ACCESS-CONTROL-ALLOW-ORIGIN to null since every request from scheme file:// is valid (which means every hybrid app can access your endpoint if it knows about your uri)?

Well, given Access-Control-Allow-Credentials: true, I believe you've got a whole auth mechanism working on the server. It should have filtered those requests without the correct auth

Hope it will help

Community
  • 1
  • 1
Ace
  • 1,093
  • 2
  • 10
  • 23
0

Use JsonP request. JsonP request enables you to do cross domain request. Here is an example.

Glenn
  • 12,741
  • 6
  • 47
  • 48
ksivamuthu
  • 307
  • 2
  • 9
0

On php side for example you need set this:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-Request-With, Set-Cookie, Cookie, Bearer');
header('Access-Control-Allow-Credentials: true');
// header('Cookie: PHPSESSID='.$_COOKIE['PHPSESSID']);
Onimo
  • 1
0

Try looking at www.5app.co.uk. Avoids use of XHR calls altogether and works reliably on mobile when data connections comes and goes. Gateway then interfaces with your client.

  • Thanks for the info. Ideally I'm not looking for a radical change in how we develop apps, but I'll see what 5app can offer us. – Tom Spencer Feb 25 '12 at 21:04