I have a backbone marionette application that makes REST api calls.
In my model when i make a api call to login i get a session value back and see the cookie being set in the browser
immediately after when i make another call to get the user information that is logged in i receive a different session or cookie value and no user is found. CORS is enabled and options calls are being made.
When i hook up the api to my other applications that were build off non backbone libraries it works fine. Does anyone know how to solve this?
Here is my post
doLogin: function( data ){
this.fetch({
data: JSON.stringify(data),
type: 'POST',
contentType: 'application/json',
error:(function (e) {
alert('error');
})
});
},
It is not clear on this piece of code but looks like your calls are going to different domains (once you mentioned CORS).
If that is really the case, I am afraid session and cookie might be different because they are probably specific only to the domain that your 1st request (doLogin) reached but not the 2nd request (fetch). More info: Sharing cookies across different domains and different applications (classic ASP and ASP.NET)
Another thing to look is if your both servers REALLY support CORS because one part of the setup is client-side and another is server-side (headers). More info on: http://www.html5rocks.com/en/tutorials/cors/
Related
In short, I have a Pharbicator setup on a domain A, but then I have to implement a service on domain B which relies on the Pharbicator.
So this is the story, I am trying to gather information using the Phabricator Conduit user.whoami API to see whether the current user is logged in. But due to various reasons I couldn't make it. I would like to know what is the correct way of doing so.
I have tried three methods but they all did not work. Here is what I tried
Method 1: Using Ajax Request
What I did is just to fire an POST Ajax request with the api token
$.ajax({
url: 'http://127.0.0.1:8080/api/user.whoami',
method: 'POST',
cache: false,
data: 'api-token='+app.PHABRICATOR_APIKEY
}).done(function(response) {
console.dir(response);
}).fail(function(jqXHR, textStatus) {
console.log(textStatus);
});
This method does not work (as expected) because it is a CORS request, and the Phabricator server does not have Access-Control-Allow-Origin header.
Although I can add that Access-Control-Allow-Origin header, but somehow this requires modifying the Phabricator source code so I would consider this as the last solution I would do.
Method 2: Using a PHP as proxy and inside use curl to request for it. So the whole request will be like Client Browser <-> PHP Proxy Script <-> Phabricator
This method is actually able to send the request and get response. But the information is wrong as it returns only the user of the API key.
If I understand it correctly, the curl is not having my browser session, so there is no way for it to really know who I (the browser client) am in Pharbricator. So I assume then the API will just fallback to use the information of API key and thus return information not I intended.
Method 3: Using the PHP library __phutil_library_init__.php provide by Phabricator
require_once '/var/www/html/phabricator/libphutil/src/__phutil_library_init__.php';
$api_token = PHABRICATOR_APIKEY;
$api_parameters = array();
$client = new ConduitClient('http://127.0.0.1:8080/');
$client->setConduitToken($api_token);
$result = $client->callMethodSynchronous('user.whoami', $api_parameters);
return $result;
Again this is nothing special, just a direct copy of code from the documentation. But again it is returning only the user the API key belongs to. And I think it is just the same as what method 2 is behaving.
I am thinking if I am understand the Conduit API in a wrong way. And more importantly I would like to know how I should implement the whole architecture so that it can gather information using the current browser session. Thank you.
I attempt to send a GET request in a jQuery AJAX request.
$.ajax({
type: 'GET',
url: /* <the link as string> */,
dataType: 'text/html',
success: function() { alert("Success"); },
error: function() { alert("Error"); },
});
However, whatever I've tried, I got XMLHttpRequest cannot load <page>. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:7776' is therefore not allowed access.
I tried everything, from adding header : {} definitions to the AJAX request to setting dataType to JSONP, or even text/plain, using simple AJAX instead of jQuery, even downloading a plugin that enables CORS - but nothing could help.
And the same happens if I attempt to reach any other sites.
Any ideas for a proper and simple solution? Is there any at all?
This is by design. You can't make an arbitrary HTTP request to another server using XMLHttpRequest unless that server allows it by putting out an Access-Control-Allow-Origin header for the requesting host.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
You could retrieve it in a script tag (there isn't the same restriction on scripts and images and stylesheets), but unless the content returned is a script, it won't do you much good.
Here's a tutorial on CORS:
http://www.bennadel.com/blog/2327-cross-origin-resource-sharing-cors-ajax-requests-between-jquery-and-node-js.htm
This is all done to protect the end user. Assuming that an image is actually an image, a stylesheet is just a stylesheet and a script is just a script, requesting those resources from another server can't really do any harm.
But in general, cross-origin requests can do really bad things. Say that you, Zoltan, are using coolsharks.com. Say also that you are logged into mybank.com and there is a cookie for mybank.com in your browser. Now, suppose that coolsharks.com sends an AJAX request to mybank.com, asking to transfer all your money into another account. Because you have a mybank.com cookie stored, they successfully complete the request. And all of this happens without your knowledge, because no page reload occurred. This is the danger of allowing general cross-site AJAX requests.
If you want to perform cross-site requests, you have two options:
Get the server you are making the request to to either
a. Admit you by putting out a Access-Control-Allow-Origin header that includes you (or *)
b. Provide you with a JSONP API.
or
Write your own browser that doesn't follow the standards and has no restrictions.
In (1), you must have the cooperation of the server you are making requests to, and in (2), you must have control over the end user's browser. If you can't fulfill (1) or (2), you're pretty much out of luck.
However, there is a third option (pointed out by charlietfl). You can make the request from a server that you do control and then pass the result back to your page. E.g.
<script>
$.ajax({
type: 'GET',
url: '/proxyAjax.php?url=http%3A%2F%2Fstackoverflow.com%2F10m',
dataType: 'text/html',
success: function() { alert("Success"); },
error: function() { alert("Error"); }
});
</script>
And then on your server, at its most simple:
<?php
// proxyAjax.php
// ... validation of params
// and checking of url against whitelist would happen here ...
// assume that $url now contains "http://stackoverflow.com/10m"
echo file_get_contents($url);
Of course, this method may run into other issues:
Does the site you are a proxy for require the correct referrer or a certain IP address?
Do cookies need to be passed through to the target server?
Does your whitelist sufficiently protect you from making arbitrary requests?
Which headers (e.g. modify time, etc) will you be passing back to the browser as your server received them and which ones will you omit or change?
Will your server be implicated as having made a request that was unlawful (since you are acting as a proxy)?
I'm sure there are others. But if none of those issues prevent it, this third method could work quite well.
you can ask the developers of that domain if they would set the appropriate header for you, this restriction is only for javascript, basically you can request the ressource from your server with php or whatever and the javascript requests the data from your domain then
Old question, but I'm not seeing this solution, which worked for me, anywhere. So hoping this can be helpful for someone.
First, remember that it makes no sense to try modifying the headers of the request to get around a cross-origin resource request. If that were all it took, it would be easy for malicious users to then circumvent this security measure.
Cross-origin requests in this context are only possible if the partner site's server allows it through their response headers.
I got this to work in Django without any CORS middleware by setting the following headers on the response:
response["Access-Control-Allow-Origin"] = "requesting_site.com"
response["Access-Control-Allow-Methods"] = "GET"
response["Access-Control-Allow-Headers"] = "requesting_site.com"
Most answers on here seem to mention the first one, but not the second two. I've just confirmed they are all required. You'll want to modify as needed for your framework or request method (GET, POST, OPTION).
p.s. You can try "*" instead of "requesting_site.com" for initial development just to get it working, but it would be a security hole to allow every site access. Once working, you can restrict it for your requesting site only to make sure you don't have any formatting typos.
I'm trying to add a few interactive things the to Django admin page via a simple RESTful api and Javascript. Should be simple, but I'm facing a weird issue where every single one of my requests from javascript is returning a 403 authorization error. Note that this only applies to js. I can hit the url from a browser just fine and do all the basic CRUD stuff.
The code is very basic.
Javascript
$.ajax({
xhrFields: {withCredentials: true},
type: 'PATCH',
url: 'path/to/my/endpoint,
data: {
aParam: someValue,
'csrfmiddlewaretoken': getCookie('csrftoken')
},
success: doSomething,
error: doSomething
});
Python
class MyObjectDetail(RetrieveUpdateDestroyAPIView):
queryset = MyObject.objects.all()
serializer_class = MyObjectSerializer
authentication_classes = (SessionAuthentication,)
permission_classes = (IsAuthenticated,)
I initially suspected that the session ID wasn't being sent, and thus that was why everything was failing due to permissions. However, the session cookie is indeed sent in the ajax POST and picked up by the Django middleware. Django pulls my Admin session with no problems. However, (after a lot of debugging) I've traced the User rewrite to a dispatch method in Django Rest Framework -- specifically, a call to self.initialize_request. After that call returns, my Admin user gets swapped out for one of rest Framework's AnonymouseUsers.
I'm totally lost. I spent about 2 hours stepping through with a debugger, but still don't understand why my user is being swapped out. Has anyone faced this before? Am I just doing something wrong?
The body of the 403 error should include a friendly message explaining what the issue is, I think in this case you will find that the message is complaining about a lack of CSRF token.
Note that this only applies to js. I can hit the url from a browser just fine and do all the basic CRUD stuff.
There are only a few things different between JS and the browser, and none of them are directly caused by Django REST framework. The one that you are probably hitting is how CSRF is dealt with in AJAX requests: the CSRF token should be passed in through a header. Right now that means changing your JavaScript to
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function addCsrfToken (xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
$.ajax({
xhrFields: {withCredentials: true},
type: 'PATCH',
url: 'path/to/my/endpoint',
data: {
aParam: someValue
},
success: doSomething,
error: doSomething,
beforeSend: addCsrfToken
});
Note that while the Django documentation recommends setting this header globally, it explicitly disallows the token from being sent in cross-origin requests. It looks like (from the withCredentials) in your code that you are accessing an API on a different domain.
Django pulls my Admin session with no problems.
This is most likely because hitting the page in your browser doesn't trigger a CSRF check, and the browsable API handles CSRF for you.
After that call returns, my Admin user gets swapped out for one of rest Framework's AnonymouseUser.
Django REST framework only deals with the anonymous user in one place, and that's when authentication fails. So while your request may be authenticated through Django, it likely isn't being authenticated through Django REST framework.
Dictionary.com provides absolutely no documentation on how to grab data from them. I'm trying to use jQuery's ajax requests, but those are not working.
They provide a URL through which I'm supposed to be able to use. I'll provide this below.
http://api-pub.dictionary.com/v001?vid=<VID>&type=random&site=dictionary
They also provide a key, which I assume that I place in the <VID> spot.
Could someone please tell me what I'm doing wrong with this? I'll provide the code I'm using below:
$("#btnGetData").click(function() {
$.ajax({
url: "http://api-pub.dictionary.com/",
type: "GET",
data: "v001?vid=<VID>&type=random&site=dictionary",
success: function() { alert("success") },
});
});
Could someone please tell me what I'm doing wrong?
You are not passing the data correctly. Also the request url is http://api-pub.dictionary.com/v001. Try this:
$("#btnGetData").click(function() {
$.ajax({
url: "http://api-pub.dictionary.com/v001",
type: "GET",
dataType: "jsonp", //For external apis
data: {"vid": <VID>,
"type":"random"
"site": "dictionary"},
success: function() {
alert("success") },
});
});
UPDATE: Maybe you're being blocked by the same origin policy as #thordarson note, in that case add:
dataType: "jsonp" to your ajax call.
Possibility 1
You don't have access to their API. According to their API page they're very selective about who they give access to their API.
We are selective about our API partners and approve use as well as
develop terms on a case-by-case basis. If you are interested in using
our API, please contact us directly [...].
Possibility 2
You're being blocked by the same origin policy. Browsers are generally very strict about requests made by JavaScript across different domains. This can be bypassed using Cross-origin resource sharing. This requires configuring the server you're requesting, so in this case it's not viable.
Your best bet would be to create a server-side script that requests the URL and then using AJAX to request the file.
Example in PHP, let's call this request_dictionary.php:
<?php
$vid = "Your API key";
$type = $_GET['type'];
$site = $_GET['dictionary'];
$request_url = "http://api-pub.dictionary.com/v001?vid=$vid&type=$type&site=$site";
echo file_get_contents($request_url);
?>
Note: You probably need to change this to fit your needs (error handling, 404s, caching etc.). This code is untested.
Then change your jQuery so that it requests your file.
$("#btnGetData").click(function() {
$.ajax({
url: "/request_dictionary.php",
type: "GET",
data: "type=random&site=dictionary",
success: function() { alert("success") },
});
});
Warning
Using an AJAX call without a proxy (as explained in possibility 2) will expose your API key. This is against Dictionary.com's API Terms of Service.
2.1 Dictionary.com will assign and deliver to your Customer Application an Application Key to access the API. All Calls must
contain such Application Key. The Application Key is uniquely
associated with each Customer Application and all versions, upgrades
and updates thereof. The Application Key is Confidential Information
as defined in these Terms and Conditions. You must maintain and keep
the Application Key in a secure, embedded manner not accessible by any
third party. The Application Key is fully revocable by Dictionary.com
at any time. Dictionary.com may block attempts to access the API with
an invalid or revoked Application Key.
I've updated the code in possibility 2 to conceal the API key by keeping it in the PHP file.
Update/Note: Simply passing dataType: 'jsonp' to enable cross-origin calls is not enough. The responding server needs to respond with an Access-Control-Allow-Origin header containing your domain (or a rule that includes your domain). Any configuration of this kind is out of your hand as the requester.
Update/Note 2: Upon investigating the returned headers from your request URL, I can see no evidence of Access-Control-Allow-Origin. This means that even if you had access to their API, you wouldn't be able to access it using AJAX. Full headers below:
GET /v001?vid=%3CVID%3E&type=random&site=dictionary HTTP/1.1[CRLF]
Host: api-pub.dictionary.com[CRLF]
Connection: close[CRLF]
User-Agent: Web-sniffer/1.0.44 (+http://web-sniffer.net/)[CRLF]
Accept-Encoding: gzip[CRLF]
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8[CRLF]
Accept-Language: en-US,en;q=0.8[CRLF]
Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7[CRLF]
Cache-Control: no-cache[CRLF]
Referer: http://web-sniffer.net/[CRLF]
I'm trying to validate a feed, using the web service that is in this question.
But browser does not allow me to send a ajax GET request to another server. And there is a one second restriction per each request in that web service, so I can't mirror requests from my server.
This is my current jQuery code:
var reqUrl = "http://validator.w3.org/feed/check.cgi?url=" + encodeURIComponent(theUrl);
$.get(reqUrl, function(data) {
// do something
});
Isn't there any other way?
Ajax calls are not valid across different domains unless you use JSONP. JQuery-ajax-cross-domain is a similar question that may give you some insight. Also as noted by Luis in the comments, JSONP has to also be implemented on the domain that you are getting the data from.
Here is an example for jquery ajax(), but you may want to look into $.getJSON():
$.ajax({
url: 'http://yourUrl?callback=?',
dataType: 'jsonp',
success: processJSON
});
Another option is CORS (Cross Origin Resource Sharing), however, this requires that the other server to enable CORS which most likely will not happen in this case.
I searched google and found this. the third answer says that:
In computing, the same origin policy is an important security concept for a number of browser-side programming languages, such as JavaScript. The policy permits scripts running on pages originating from the same site to access each other's methods and properties with no specific restrictions, but prevents access to most methods and properties across pages on different sites.(source)
you'd better see the answers of this question.
I think you can't use JSONP because you haven't any access to W3C script.
Update (explanations)
I the question I linked to there is another way that I can explain it to you. if you set Access-Control-Allow-Origin header to * as the answer said you can send requests to another domains. and to use it easily in an MVC application you can see this solution. Good luck
Update2
to allow just http://validator.w3.org/ you just should set Access-Control-Allow-Origin to http://validator.w3.org/
for more details as answer said go here.
As said you can use JSONP but the endpoint must also implement it, And its only used if you are requesting json data from the call. It looks like you are retrieving html.
You can also implement a simple proxy in your domain that pulls the data from the external location and serves it to the ajax call. You can develop a simple proxy in php using for instance CURL.
Make sure you understand the implications of this security wise making sure for instance that you protect your proxy to only make calls to that external url (whitelisting).
Update: I just noticed you cannot use the proxy solution. And after following the link you have suggested I have came across CORS, which I didnt event know about. So apparentry you can set some headers when you are serving the pages in your domain that will instruct the browser that requests to some domains can be done.
Check this page for how to implement it:
http://enable-cors.org/
I have read that you might have to tweak it a bit to work with IE but it seems that all browsers are now implementing it.
I know this is an old question, but I myself have been trying to create AJAX requests to validator.w3.org as well, hit the exact same issues and stumbled on this SO question.
However, I did find a solution;
As people have already stated, the main problem here is that the server must issue valid CORS headers, i.e.
Access-Control-Allow-Origin: *
I used Fiddler to check the response headers from validator.w3.org and sure enough, the headers were not set. However, they also have another tool that does at validator.w3.org/nu/.
Here is an example: http://codepen.io/DDN-Shep/pen/ogdGgO/
$('form').on('submit', function(e) {
e.preventDefault();
var data = new FormData(this);
$.ajax({
url: 'http://validator.w3.org/nu/', // Trailing '/' is important or you get a 301 HTTP response status code
type: 'POST',
data: data,
processData: false, // Required for jQuery & FormData
contentType: false, // Set by FormData, required by the server
success: function(data, status, xhr) { /* TODO */ },
error: function(xhr, status, error) { /* TODO */ },
complete: function(xhr, status) { /* TODO */ }
});
});
If you are ever unsure whether or not a server allows CORS, you can use this very helpful online tool;
test-cors.org = http://client.cors-api.appspot.com/client