I am using the Ionic framework to build an html5 app that is then deployed to an iOS device with PhoneGap. Everything was working great, then we switched some server addresses that were being used to get data to include a port number in the address and the HTTP method was switched from a GET to a POST. After the switch we tested locally in the browser and it was pulling data with no problems. When we deployed to the device we saw nothing in the logs when the request was executed, and the success or error handler was never reached. In the code below only "HERE1" is printed and everything stops.
console.log("HERE1");
$http.post("http://XX.XX.XX.XX:81/api/Authenticate", null ,{ headers: {'Authorization': 'Basic ' + encodedNamePw} })
.success(function (data, status, headers, config) {
console.log("HERE2");
})
.error(function (data) {
console.log("HERE3");
});
I then tried to perform a GET to the same address and I got a response, saying that I obviously had the incorrect method. I then tried a POST to the address without a port and I got response, which was again an error, but at least I got a response. So to summarize it works locally in browser, it works on the iOS device with a GET request and a PORT, but it does not make any request when I use the POST method. I did try to escape the colon but that did not help. I'm pretty baffled by this one so any thoughts are appreciated. Thanks!
After much pain an agony I found the issue and the options available. The issue was that we were getting a 401 response and a www-authenticate http header. When this is sent by the server apparently iOS tries to pop a login dialog, which is blocked by PhoneGap. Instead of hitting the error callback method it just eats it and you never get any feedback that the request has completed.
The best solution requires you to have access to the server you are calling. If you do, and you can update the response to not send a www-authenticate header for 401 responses then you are golden and it works as expected. If you can't then you have the option to set the async option to false on the request. This approach will block at the UI, which is not ideal, but may be acceptable if it is just a login request. Best of luck.
Related
I'm stumped. I'm making a POST request to an API using fetch. It's a Vue3 app built with Vite. Everything works locally, also in Vite preview, but when I make a fetch request in production using any method other than GET, the request hangs. The request is logged in network, but absolutely nothing happens - no error, nothing. There's not even any connection information - like the connection doesn't happen at all.
The endpoint is a cloudflare worker. It works fine using GET, the API works fine using curl, however browsers simply won't even begin to connect.
There's no error at all, just an indefinite hang.
Weird one this. The cloudflare worker was cloning the request, passing it to another service, then didn't read the response which caused the cloned request to cancel. This seemed to cause execution to hang for the first request. Didn't happen with GET requests.
My app is a cordova/ionic app. Recently one of the APIs my app pulls from had an authentication error...the token is revoked. On Android the service responsible for the API call returns an API error, the error is processed, and the app continues...but on iOS the auth error is now generating the following type of iOS error which causes the service to just spin its wheels and never return anything:
CredStore - preformQuery = Error copying matching creds Error=-25300
I have never seen this error before. This is not a standard API error, the rest of my API service call handles API errors (including auth errors), but this error isn't an API error, its an iOS error which is preventing the $http response from properly returning. I do know whats causing this or how to deal with it. Its like the auth error is triggering some kind of iOS credentials error which in turn is causing the $http(req) to just hang waiting for a response....
var req = {
method: 'POST',
url: 'https://api.blah.com/oauth/token',
headers: {"Content-Type":"application/JSON", Authorization: "Basic " + blahCreds},
data: {"grant_type":"client_credentials","scope":"public"}
}
return $http(req)
.then(function(response){
The only way I can get past this issue on iOS is to add: timeout:httpTimeout, to the $http(req) - after 30 seconds of no response that API just times out and control is returned to the app to process the timeout (and other data from other APIs returning valid data).
To further validate the response is never being returned, when I monitor my app in Safari -> Develop, I see the entry for url https://api.blah.com/oauth/token just spinning and spinning waiting for some response, but in Xcode I see the above error thrown.
I attach on every outgoing request an Authorization header whose value is 'Bearer {access_token}' with the following command : window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('access_token');
This works fine everywhere except on iOS where the header is not attached to the request.
I also tried to attach it on a single request, and same issue.
The header is not present in the request and the server returns a 401 Unauthorized error.
The bug happens whatever the browser is on iOS : Safari, Chrome, Firefox. It is really specific to iOS (my version is 11.3.1). No problem on Android or on Desktop (MacOS, Windows).
I made some debugging with the iOS Simulator (where the problem also happens) : there is an item named access_token in the localStorage so the problem is not related to that.
What is happening??
thanks
As you mentioned, this bug seems to happen only on iOS/Safari. We're also facing this issue with axios in our React Native app.
It happens only with GET requests (authorization token gets sent alright with POST and PUT requests).
So you have two options:
Use a different header key, e.g. Authorization-Fake-X as suggested here.
Change the type of your requests (e.g. POST or PUT) if this issue occurs only with GET requests. This is an ugly option because you'll break the REST API behaviour.
Update:
The issue seems to have been fixed in October 2021, according to this SO answer. Still, we have to fix it for people using older versions of Safari and iOS.
We are creating a Ionic app with a Symfony api and we have a problem with $http when login on iOS. When login or password are not corrects, $http never received response from the api and timeout 30seconds later with status 0, althought everything works fine on Android with the same code (api responses are the same regardless of the platform). We tries a lot of different things but it seems like iOS refuses to receive the response for this particular request. Other requests works normally, response and status are received as expected. Below is the login request code
$http.defaults.headers.common.Authorization = 'Basic ' + Base64.encode(user + ':' + password);
$http.get("https://myserver.com/api/login")
.success(function(data, status) {
console.log("success");
})
.error(function(error, status) {
console.log("error");
});
Do somebody has ever encounter this kind of problem with iOS or even know how to fix it (or at least to debug it !)?
I don't know what code can make you help us so let me know !
EDIT:
Not found the solution yet but now I understand the problem !
When http basic response's status is 401, it set a header named WWW-Authenticate. But Cordova don't like this header (probably because of the "" in the header value) so didn't send the response to the app ...
The apache issue:
https://issues.apache.org/jira/browse/CB-2415
Another SO post related:
Handling HTTP 401 with WWW-Authenticate in Cordova / Ionic iOS application
So now my problem has changed : How can I remove a header from Symfony Http Basic Authentification's response ?
It's a bit hard to debug without an error to look at but you could try:
Assuming your url is not https based, your error is most likely due to ios blocking non http requests. It can be fixed by adding this cordova hook
Try adding your domain to the whitelist in config.xml like this <allow-navigation href="https://myserver.com/*"/>
I have a phonegap app which connects to a web service and authenticates using http basic authentication. It is built using phonegap build and targets Android and iOS.
On a login view, an ajax request fires against the remote server to check if credentials are correct, then if so, logs the user in to the main application.
This completes successfully in ripple emulator on desktop pc and when also when deployed onto an Android device.
However, when the app is deployed onto an iOS device (ipod touch) the authentication request simply does not ever complete. Using phonegap remote debugger I can see that the ajax request starts but never completes. It is always in a pending state. I use jquery ajax success, error and complete handlers for the request, but none of them are ever hit so I don't get the chance to see any error messages returned from the server. The request never seems to complete.
I have tried making ajax requests to other remote web sites to test that my app can communicate and they succeed, so it doesn't seem as though I have white-listing issues.
Any ideas of what the issue could be?
Please read the update to this answer at bottom.
Original answer
I have found what the issue is and managed to get basic authentication working.
The issue was that the web server was expecting basic authentication details to be preemptively sent with the request.
To do this use the 'headers' property of jquery ajax as shown below:
$.ajax
({
type: "GET",
url: "https://webserver/authenticate",
headers: {
"Authorization": "Basic " + btoa(USERNAME + ":" + PASSWORD)
}
})
.done(function(){
alert('Authenticated!')
})
.fail(function(){
alert('Error!')
});
See related answer:
https://stackoverflow.com/a/11960692/1463497
Update
I found in the end that there is no reliable way of using basic authentication in a web view in iOS. I.e everything is fine if correct credentials are entered but when they are not and the 401 challenge response comes along, iOS web view can't seem to handle it.
In the end, the web service authentication was implemented by simply passing 'username' and 'password' parameters as part of the url:
url: "https://webserver/authenticate?username=abc&password=123"
This is the only consistent way I found of getting authentication to work across iOS and Android in a web view (by getting rid of basic authentication altogether). This of course meant updating the web service itself to accept authentication in this way.