Ionic HTTP Post v. Angular HTTP Post

I am working on getting an authentication token from my web API. I am taking code from my Angular web application that is calling the API and retrieving the token successful. I have put my code in a service that I have moved over to my ionic application.

    var _login = function (loginData) {

       var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;

       if (loginData.useRefreshTokens) {
           data = data + "&client_id=" + ngAuthSettings.clientId;
       }

       var deferred = $q.defer();
       $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {

           if (loginData.useRefreshTokens) {
               //angular version commented and replaced with local storage for ionic
               //localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName, fullName: loginData.fullName, refreshToken: response.refresh_token, useRefreshTokens: true });
               window.localStorage['authorizationData'] = JSON.stringify({ token: response.access_token, userName: loginData.userName, fullName: loginData.fullName, refreshToken: response.refresh_token, useRefreshTokens: true });
           }
           else {
               //angular version commented and replaced with local storage for ionic
               //localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName, fullName: loginData.fullName, refreshToken: "", useRefreshTokens: false });
               window.localStorage['authorizationData'] = JSON.stringify({ token: response.access_token, userName: loginData.userName, fullName: loginData.fullName, refreshToken: "", useRefreshTokens: false });
           }
           _authentication.isAuth = true;
           _authentication.userName = loginData.userName;
           _authentication.fullName = loginData.fullName;
           _authentication.useRefreshTokens = loginData.useRefreshTokens;

           deferred.resolve(response);

       }).error(function (err, status) {
           _logOut();
           deferred.reject(err);
       });

       return deferred.promise;

   };

The issue I have, is that while this works fine in angular, it does not work in ionic. I am getting the error back that this is an unsupported grant type. When I inspect the request in fiddler, it is also different, leading me to believe that there is something I need to do differently in this code for it it work in ionic. Here are the two raw requests:

Angular’s Request

POST http://localhost:26264/token HTTP/1.1
Host: localhost:26264
Connection: keep-alive
Content-Length: 53
Accept: application/json, text/plain, */*
Origin: http://evil.com/
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2500.0     Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:32150/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8

grant_type=password&username=myusername&password=mypw

Ionic Request

OPTIONS http://localhost:26264/token HTTP/1.1
Host: localhost:26264
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://evil.com/
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko)     Version/8.0 Mobile/12A4345d Safari/600.1.4
Access-Control-Request-Headers: accept, authorization, content-type
Accept: */*
Referer: http://localhost:8100/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

I notice that in Ionic’s request it doesn’t even seem to send the data to the post.

So, any assistance would be greatly appreciated!

Thanks!

Try this one:

var headers = {
   'Authorization': 'Bearer ' + token
};
var requestData = {
    username : '',
    password : '',
    client_id : ''
}

var req = {
   method: 'GET' OR 'POST',
   url: '',
   headers: headers
};

//if it is a GET request set the parameters like this
  req.params = requestData;
//if it is a POST request set the parameters like this 
  req.data = requestData;

var deferred = $q.defer();
$http(req).then(function(response) {
    deferred.resolve();
}, function(error) {
    deferred.reject();
});

Both services are the same (the $http service is the same between your “angular” app and your “ionic” app).

The Ionic request that you are pasting in is what’s called an OPTIONS request, a “pre-flight” request.

It’s sent in situations where the browsers wants to check whether it’s safe to send the actual “post” request.

So it’s send specifically in situations where the originating request and the target server are from different domains and therefore needs to check if CORS applies.

I suspect you are not seeing this in your angular app because in your angular app the request and api are from the same server (e.g. js hosted at example.com and api its calling is hosted at example.com).

So the OPTIONS is perfectly normal, i’d expect either an error to get printed if the response to the options indicates your API doesn’t support cross domain requests OR the options to be successful and then it tries the POST.

Yes, originally I was getting a CORS error, which confused me, since my web api is setup to all all origins. I got around the error by disabling cors in my chrome browser with an addon. What does my ionic call need to look like in this case? I am also going to try the suggestion above.

Thanks!!!

Hmm… not sure to be honest, if you have control over your API then you should be able to solve this problem by setting some headers in the response, what was specifically the request and response from you server when trying it w/out the plugin? I.e. the detailed into from the network tab in chrome.

The original error was the following:

XMLHttpRequest cannot load http://localhost:26264/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access. The response had HTTP status code 400.

That’s telling me that the API hasn’t set the access-control-allow-origin header to *.

You mentioned you’ve already added this header to the API however i reckon you also need to add the header for all OPTION type requests, maybe you just added it for POST requests only?

You can check the headers actually sent and received in the response. In chrome > network tab > choose xhr > click on the request e.g. like this:-

Curious, why is it that the Ionic is sending the OPTIONS instead of POST, even though I am telling it to send a POST?

See my first reply, it’s because you are now calling api from a different domain…

I suppose the confusion I have is that technically I was calling it from a different domain in my angular web application. Even though it is on the same box when I am debugging it, one is at something like localhost:25256 and one is like localhsot:43554. Since the ports are different as I understand it, it is a different domain. In fact, until I enabled cors on the web api the angular app didn’t work. So something else has to be causing it besides just being the different domain. I have also been told that perhaps this will go away entirely once I deploy my ionic app. But I do still have to figure it out for debugging. I will check to see if perhaps i only have CORS enabled for certain requests.

Thanks again for all your help. I am not as strong as I should be with HTTP so it has me a bit confused.

Don’t beat yourself up CORS is a gigantic pain in the butt!

Not sure exactly what’s going on, i could be wrong that OPTIONS is not sent if both in the same domain, it’s late and i’m a bit tired but for sure when you run on a device or in an emulator the problem will disappear - cause it’s your desktop browser thats performing the check and disallowing the call not your api server, but makes it a total pain so develop with if you can’t use the browser.

I still think if you’ve got control of the API it’s way way better to figure out what’s going on, but the ionic team have a work around for people who don’t control the api (it creates a local proxy server) so you can try that as well.

Thanks again for all your help. I am on vacation this week, so I will have to wait to proceed further on this until mid next week. I will let you know how things go from that point. I greatly appreciate your willingness to help me through this though!!

@robertlair, did you fixed it cos I am having the same problem now and I am frustrated… Very very

Yes, I solved I by using Ionic Proxy. http://blog.ionic.io/handling-cors-issues-in-ionic/

This doesn’t work for me. This is the error I now get after adding sending POST to the server

XMLHttpRequest cannot load http://url.com?action=test. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:8100’ is therefore not allowed access.

Looks like the proxy perhaps was not setup correctly as you are still getting the CORS error. Setting up the proxy was a bit confusing in my opinion. Basically what you will be doing is calling your API using the localhost:8100/proxypath, then the proxy will redirect that to the actual API location.

As an example:

"proxies": [
{
  "path": "/api",
  "proxyUrl": "https://apiurl.azurewebsites.net/"
}],

Then in your application, you would call your api with localhost:8100/api.

Hopefully this helps.
Bob

I do not have the php script in my local server. It is online

This is my ionic.project file

> {
>   "name": "proxy-example",
>   "app_id": "",
>   "proxies": [
>     {
>       "path": "/api",
>       "proxyUrl": "apireferencepoint which is a url"
>     }
>   ]
> }

and my services.js looks like this

> angular.module('starter.services', [])

>  // For the real endpoint, we'd use this
> .constant('REF', {
>   url: 'localhost:8100/api'
> })