I'm developing a hybrid mobile application using AppGyver Steroids and AngularJS. In this mobile application I need to use the RESTful APIs of another project which I've developed; here is an example request I'm sending from the mobile app:
var data = {
'email': $scope.userEmail,
'password': $scope.userPassword
}
$http.post('http://example.com/api-auth-token/?format=json', data)
.success(function(data, status, headers, config) {
alert(data);
$location.path('/profile');
})
.error(function(data, status, headers, config) {
// show error details
navigator.notification.alert(
'Authentication failed. (' + status + ')',
null,
'Error',
'Okay'
);
});
This request reaches the server perfectly well and a valid response is generated with status code 200... at least that is the case when I check the server logs. The mobile application shows a message box saying 'Authentication failed. (404)' which contradicts with what the server logs are stating.
Edit: I have also developed a native iOS app which uses these APIs and it works without any problems.
Turns out Access-Control-Allow-Origin header was missing from responses generated by the server and this was the main culprit.
Related
I started an AngularJs App and to retrieve some data from database I'm using NodeJS (totally new to me), on the console of NodeJS it works and also typing the URL directly in the browser but when I try to get the information needed using http.get() in AngularJS using the same URL in the browser I get 404 not found.
I figured it would be a cors problem so I added
require('cors') in the nodeJS app and still doesn't work
Can anyone help me with that ?
Am I right making separate apps for Angularjs in front-end and NodeJS in the Backend or should I assemble them in only one application ?
Thank you for your help
This is the AngularJS code:
$scope.keyLoad = function () {
$http.get("localhost:8080/product")
.success(function (response) {
$scope.keys = response;
console.log(response)
})
};
$scope.keyLoad();
I get 404 not found. I figured it would be a cors problem
If it says it is a 404 error then it is a 404 error and not a CORS problem.
Look at your code:
$http.get("localhost:8080/product")
That URL is missing the scheme. It is a relative URL.
You are going to be requesting something like http://example.com/myapp/localhost:8080/product.
Put http:// or https:// in front of it.
You should use $http service.
For example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Or
$http.get('/someUrl', config).then(successCallback, errorCallback);
I am trying to create a user login/signup on my remote app that accesses user data on Salesforce. I am doing this from javascript via Salesforce REST API. This was not working using requests straight from javascript due to CORS restrictions, so I found this example. My code is as follows:
var result = sforce.connection.login('example#provider.com', 'pass'+'securityToken');
sforce.connection.init(result.sessionId, 'https://login.salesforce.com/services/oauth2/token')
sforce.connection.remoteFunction({
url: 'https://login.salesforce.com/services/oauth2/token',
requestHeaders: {
"Authorization" : "Bearer " +__sfdcSessionId,
"Content-Type":"application/json",
"Connection":"Keep-Alive"
},
method: "GET",
onSuccess : function(response) {
console.log("Success " + response)
},
onFailure: function(response) {
console.log("Failed " + response)
}
});
When I run this code I get the following errors:
1) Refused to set unsafe header "User-Agent"
2) POST http:///services/Soap/u/31.0 404 (Not Found)
3) Remote invocation failed, due to:
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /services/Soap/u/31.0 was not found on this server.</p>
</body></html>
status code:
Using a code editor I can see that the errors are occurring after hitting the sforce.connection.login() script and never making it to the sforce.connection.init() script in my code.
How do I resolve this issue so that I may log a user in from my remote web app and gain access to the user information within salesforce?
It seem your issue is like the one in this post
XMLHttpRequest isn't allowed to set these headers, they are being set automatically by the browser. The reason is that by manipulating these headers you might be able to trick the server into accepting a second request through the same connection, one that wouldn't go through the usual security checks - that would be a security vulnerability in the browser.
I'm building an AngularJS (1.2.16) web app with a RESTful API, and I'd like to send 401 Unauthorized responses for requests where authentication information is invalid or not present. When I do so, even with an HTTP interceptor present, I see the browser-presented basic "Authentication Required" dialog when an AJAX request is made via AngularJS. My interceptor runs after that dialog, which is too late to do something useful.
A concrete example:
My backend API returns 401 for /api/things unless an authorization token is present. Nice and simple.
On the AngularJS app side, I've looked at the docs and set up an interceptor like this in the config block:
$httpProvider.interceptors.push(['$q', function ($q) {
return {
'responseError': function (rejection) {
if (rejection.status === 401) {
console.log('Got a 401')
}
return $q.reject(rejection)
}
}
}])
When I load my app, remove the authentication token, and perform an AJAX call to /api/things (to hopefully trigger the above interceptor), I see this:
If I cancel that dialog, I see the console.log output of "Got a 401" that I was hoping to see instead of that dialog:
Clearly, the interceptor is working, but it's intercepting too late!
I see numerous posts on the web regarding authentication with AngularJS in situations just like this, and they all seem to use HTTP interceptors, but none of them mention the basic auth dialog popping up. Some erroneous thoughts I had for its appearance included:
Missing Content-Type: application/json header on the response? Nope, it's there.
Need to return something other than promise rejection? That code always runs after the dialog, no matter what gets returned.
Am I missing some setup step or using the interceptor incorrectly?
Figured it out!
The trick was to send a WWW-Authenticate response header of some value other than Basic. You can then capture the 401 with a basic $http interceptor, or something even more clever like angular-http-auth.
I had this issue together with Spring Boot Security (HTTP basic), and since Angular 1.3 you have to set $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest'; for the popup not to appear.
For future reference
I've come up with this solution when trying to handle 401 errors.
I didn't have the option to rewrite Basic to x-Basic or anything similar, so I've decided to handle it on client side with Angular.
When initiating a logout, first try making a bad request with a fake user to throw away the currently cached credentials.
I have this function doing the requests (it's using jquery's $.ajax with disabled asynch calls):
function authenticateUser(username, hash) {
var result = false;
var encoded = btoa(username + ':' + hash);
$.ajax({
type: "POST",
beforeSend: function (request) {
request.setRequestHeader("Authorization", 'Basic ' + encoded);
},
url: "user/current",
statusCode: {
401: function () {
result = false;
},
200: function (response) {
result = response;
}
},
async: false
});
return result;
}
So when I try to log a user out, this happens:
//This will send a request with a non-existant user.
//The purpose is to overwrite the cached data with something else
accountServices.authenticateUser('logout','logout');
//Since setting headers.common.Authorization = '' will still send some
//kind of auth data, I've redefined the headers.common object to get
//rid of the Authorization property
$http.defaults.headers.common = {Accept: "application/json, text/plain, */*"};
I'm creating a web app using AngularJS. To test it, I'm running the app in a NodeJS server, using angular-seed template.
In this app, I need to send a JSON message to another host, via POST request, and get the response, so, I'm using CORS.
My request is done by implementing a service that uses AngularJS http service (I need the level of abstraction that $http provides. So, I don't use $resource).
Here, my code. Please pay attention to the fact that I modify $httpProvider to tell AngularJS to send its requests with the appropriate CORS headers.
angular.module('myapp.services', []).
// Enable AngularJS to send its requests with the appropriate CORS headers
// globally for the whole app:
config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
/**
* Just setting useXDomain to true is not enough. AJAX request are also
* send with the X-Requested-With header, which indicate them as being
* AJAX. Removing the header is necessary, so the server is not
* rejecting the incoming request.
**/
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]).
factory('myService', function($http) {
return {
getResponse: function() {
var exampleCommand = JSON.stringify({"foo": "bar"});
// This really doesn't make a difference
/*
var config = {headers: {
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Headers': 'Content-Type, Content-Length, Accept',
'Content-Type': 'application/json'
}
};
*/
//return $http.post(REMOTE_HOST, exampleCommand, config).
return $http.post(REMOTE_HOST, exampleCommand).
success(function(data, status, headers, config) {
console.log(data);
return data;
}).
error(function (data, status, headers, config) {
return {'error': status};
});
}
}
});
The problem is I can't make it work. I always get this error message:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at REMOTE_HOST. This can be fixed by moving the
resource to the same domain or enabling CORS.
But if I do a simple jQuery AJAX call like this:
$.ajax(REMOTE_HOST,
{
dataType: "json",
type: "POST",
data: exampleCommand,
success: function(data) { console.log(data); },
error: function(request, textStatus, errorThrown) { console.log("error " + textStatus + ": " + errorThrown);}
});
It works fine.
So, my questions:
- How do I allow cross-site requests in an AngularJS running under NodeJS?
UPDATE: Thanks to Dayan Moreno Leon's response.
My problem is I need to add cors support to my server. I'm using NodeJS http-server for development and lighttpd for production.
- Why does the simple jQuery POST request work but AngularJS POST request doesn't?
I guess jQuery AJAX requests are cross-domain by default. Not really sure yet.
Many thanks in advance
CORS is not handled on the client but in the server you need to allow CORS on your nodejs app where your angular app is trying to POST. you can try using cors module if you are using express
https://www.npmjs.org/package/cors
other whise you need to check for the options method and return 200 as a response
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
Why does the simple jQuery POST request work but AngularJS POST request doesn't?
jQuery uses simple requests while AngularJS uses preflighted requests
In your angular code you can add set Content-Type to application/x-www-form-urlencoded and encode your data using $.param
I'm using Angular's $http service to get some data from a remote data source, but logging out the results to the console gives empty strings.
The function looks as follows:
$http.get('/some/url/')
.success(function(data/*, status, headers, config*/) {
console.log('Success! data: ', data);
})
.error(function(data, status, headers, config) {
console.log('failed http req!');
console.log('data: ', data);
console.log('status: ', status);
console.log('headers: ', JSON.stringify(headers()));
console.log('config: ', JSON.stringify(config));
});
I'm purposfully calling a URL I know does not exist and expecting to get a 404. When running from the browser (using cordova serve) I see all the error data printed out to the console.log.
I'm using Cordova ~3.4
I've installed the console plugin for Cordova.
I'm viewing the Android device log using adb logcat set to debugging.
Any ideas?
Update: I tried on all 4 variables to use JSON.stringify, just to see if that might work out of sheer luck in a moment of frustration... left them on the headers() and config since they're objects anyway. I still didn't get any print out on the data or status, which still puzzles me...
Cordova's console.log accepts only one argument (At least on Android) and that's why I've been getting only partial logging. Changing the code to:
$http.get('/some/url/')
.success(function(data/*, status, headers, config*/) {
console.log('Success! data: ' + data);
})
.error(function(data, status, headers, config) {
console.log('failed http req!');
console.log('data: ' + data);
console.log('status: ' + status);
console.log('headers: ' + JSON.stringify(headers()));
console.log('config: ' + JSON.stringify(config));
});
solved the problem.
The success and error callbacks actually decompose the response object into 4 arguments and pass them as parameters.
You should use the .then syntax for handing success and error:
$http(...).then(success,error)
The first argument to your error callback is the raw response object, which is in JSON format. You can extract some error information from it.
The success and error callbacks are convenient however, so moving this to a response interceptor might make more sense.
Passing multiple args to console.log is not recommended way in Cordova. You can contact the log instead. The reason is, Cordova uses WebKit under the hook, and WebKit's onConsoleMessage method has only one argument and it will always print the first. You can find more details on official docs for android.