Capture redirect location of javascript XMLHttpRequest - javascript

I know that you can't, when using an XMLHttpRequest, intercept a redirect or prevent it, as the browser will transparently follow it, but is it possible to either
A. Determine whether a request redirected, or
B. Determine where it redirected to? (assuming that the response gives no hints)
Example code:
$.post("/my-url-that-redirects/", {},
function(response, statusCode, xmlHttpRequest){
//Somehow grab the location it redirected to
}
);
In my case, firebug will first show a POST to the url, then a GET to the redirected url. Can that GET location be captured?

1) Use different status code than 301 (2**) (if request by ajax) and handle redirection on client side:
var STATUS = {
REDIRECT: 280
};
$.post('/redirected', {}, function(response, status, request) {
if (status == STATUS.REDIRECT) {
// you need to return the redirect url
location.href = response.redirectUrl;
} else {
$('#content').html(request.responseText);
}
});
2) DO NOT REDIRECT:
I use that in "redirect pattern" = redirecting after post request (you don't want to allow user to refresh the post request, etc..)
With ajax request, this is not necessary, so when the post request is ajax, I do forward instead (just forward to different controller - depends on your server-side framework, or what you are using...). POST requests are not cached by browsers.
Actually, I don't know what's the reason you need that, so this might not be so useful for you. This is helpful when server returns different responses for ajax requests than common requests, because when browser redirect ajax request, the redirected request is not XMLHttpRequest...
[updated]
You can access headers (of redirected request) like that:
$.post('redirected', {}, function(r, s, req) {
req.getAllResponseHeaders();
req.getResponseHeader('Location');
});
There should be 'Location' header, but it depends on the server, which headers are sent back...

After 4 years now it's possible to find the last redirect location using responseURL from XHR instance in Chrome 42+ (Opera 29+) and Firefox 39+ but it's not available in IE, Edge or safari yet.

Related

React render response page upon post request [duplicate]

I am creating an social login page with an Access Management (AM) server.
When user click on the login button then I make a fetch http post call to AM server. AM server generates a HTTP 301 redirect response with auth cookies to the social login page. I need to follow somehow this redirect response and show the new content in the web browser.
UI: ReactJS
Request:
POST /api/auth/socialauth/initiate HTTP/1.1
Host example.com
User-Agent Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0)
Accept */*
Accept-Language en-US,en;q=0.5
Accept-Encoding gzip, deflate
origin http://web.example.com:8080
Referer http://web.example.com:8080/myapp/login
Cookie authId=...; NTID=...
Response
HTTP/1.1 307 Temporary Redirect
https://www.facebook.com/dialog/oauth?client_id=...&scope=public_profile%2Cemail&redirect_uri=http%3A%2F%2Fam.example.com%3A8083%2Fopenam%2Foauth2c%2FOAuthProxy.jsp&response_type=code&state=qtrwtidnwdpbft4ctj2e9mv3mjkifqo
React code:
initiateSocialLogin() {
var url = "/api/auth/socialauth/initiate";
fetch(url, { method: 'POST' })
.then(response => {
// HTTP 301 response
// HOW CAN I FOLLOW THE HTTP REDIRECT RESPONSE?
})
.catch(function(err) {
console.info(err + " url: " + url);
});
}
How I can follow the redirect response and show the new content in the web browser?
Request.redirect could be "follow", "error" or "manual".
If it is "follow", fetch() API follows the redirect response (HTTP
status code = 301,302,303,307,308).
If it is "error", fetch() API treats the redirect response as an
error.
If it is "manual", fetch() API doesn't follow the redirect and returns
an opaque-redirect filtered response which wraps the redirect
response.
Since you want to redirect after a fetch just use it as
fetch(url, { method: 'POST', redirect: 'follow'})
.then(response => {
// HTTP 301 response
})
.catch(function(err) {
console.info(err + " url: " + url);
});
Have a look at properties url redirected of Response object:
Doc says that this is
"Experimental. Expect behavior to change in the future"
The url read-only property of the Response interface contains the URL
of the response. The value of the url property will be the final URL
obtained after any redirects.
In my experiments, this 'url' property was exactly the same as the value of Location header in Chrome (Version 75.0.3770.100 (Official Build) (64-bit)) Network console.
The code to deal with redirecting link my look like this:
fetch(url, { method: 'POST' })
.then(response => {
// HTTP 301 response
// HOW CAN I FOLLOW THE HTTP REDIRECT RESPONSE?
if (response.redirected) {
window.location.href = response.url;
}
})
.catch(function(err) {
console.info(err + " url: " + url);
});
I tested it working with react.js same-origin script with fetch AJAX call facing redirects 302 from server.
P.S. In SPA apps, redirect responses are unlikely, maybe this is the reason why ajax vendors apply little attention to this functionality.
See also these discussions:
here
here
It is not possible to follow a redirect to a new HTML page with javascript.
fetch(url, { method: 'POST', redirect: "follow" });
will simply perform another request to the redirected location which will be returned as data and not rendered by the browser. You might expect to be able to use { redirect : "manual" }, get the redirected location from the response and navigate to the page with Javascript, but unfortunately the redirected location is not returned, see https://github.com/whatwg/fetch/issues/763.
I have a similar issue and I believe that the answer for fetch inside React is the same as it is for ajax inside JQuery - if you are able to detect that the response is a redirect, then update the window.location.href with the response.url
See for example: How to manage a redirect request after a jQuery Ajax call
Note that 'if you are able to detect that the response is a redirect' might be the tricky part. Fetch responses may contain a 'redirected' flag (see https://developer.mozilla.org/en-US/docs/Web/API/Response) but I've found that is not the case in Chrome. I also find in Chrome I get a 200 status response rather than a redirect status - but that could be something with our SSO implementation. If you are using a fetch polyfill with IE then you'll need to check whether response.url is included or not.
My solution for this scenario was sent the url I want to be redirected as a parameter and then receiving it in body response, instead of using redirect from server
I tried using fetch to redirect to the url but this method didn't work, so I ended up using a different method to get the redirect to work. Inside your react component follow the following steps:
Step 1: create a state variable:
const [_id,setID]=useState('')
Step 2: create a method that updates the state:
function uidValue(event) {
const endpoint = '/api/users/'+ event.target.value // this will be your url, so set it right.
setID(endpoint)
}
Step 3: in the return section of your component add an onChange listener to the username(id in my case):
<input id="uid" type="text" name=":_id" placeholder=":_id" onChange={uidValue} />
Step 4: make sure all your inputs are inside a form, and add an action attribute to the form tag that sets the URL you want it to navigate to using your state:
<form action={_id} id="login info" method="post" >
Fetch is not able to get the redirect URL, but XMLHttpRequest can.
if you want to get the redirect URL, you can try this code:
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/auth/socialauth/initiate");
xhr.send();
xhr.onreadystatechange = function () {
if (this.readyState === this.DONE) {
console.log(this.responseUR);
this.abort();
}
};

Failed posting data with axios [duplicate]

I'm trying to load a cross-domain HTML page using AJAX but unless the dataType is "jsonp" I can't get a response. However using jsonp the browser is expecting a script mime type but is receiving "text/html".
My code for the request is:
$.ajax({
type: "GET",
url: "http://saskatchewan.univ-ubs.fr:8080/SASStoredProcess/do?_username=DARTIES3-2012&_password=P#ssw0rd&_program=%2FUtilisateurs%2FDARTIES3-2012%2FMon+dossier%2Fanalyse_dc&annee=2012&ind=V&_action=execute",
dataType: "jsonp",
}).success( function( data ) {
$( 'div.ajax-field' ).html( data );
});
Is there any way of avoiding using jsonp for the request? I've already tried using the crossDomain parameter but it didn't work.
If not is there any way of receiving the html content in jsonp? Currently the console is saying "unexpected <" in the jsonp reply.
jQuery Ajax Notes
Due to browser security restrictions, most Ajax requests are subject to the same origin policy; the request can not successfully retrieve data from a different domain, subdomain, port, or protocol.
Script and JSONP requests are not subject to the same origin policy restrictions.
There are some ways to overcome the cross-domain barrier:
CORS Proxy Alternatives
Ways to circumvent the same-origin policy
Breaking The Cross Domain Barrier
There are some plugins that help with cross-domain requests:
Cross Domain AJAX Request with YQL and jQuery
Cross-domain requests with jQuery.ajax
Heads up!
The best way to overcome this problem, is by creating your own proxy in the back-end, so that your proxy will point to the services in other domains, because in the back-end not exists the same origin policy restriction. But if you can't do that in back-end, then pay attention to the following tips.
**Warning!**
Using third-party proxies is not a secure practice, because they can keep track of your data, so it can be used with public information, but never with private data.
The code examples shown below use jQuery.get() and jQuery.getJSON(), both are shorthand methods of jQuery.ajax()
CORS Anywhere
2021 Update
Public demo server (cors-anywhere.herokuapp.com) will be very limited by January 2021, 31st
The demo server of CORS Anywhere (cors-anywhere.herokuapp.com) is meant to be a demo of this project. But abuse has become so common that the platform where the demo is hosted (Heroku) has asked me to shut down the server, despite efforts to counter the abuse. Downtime becomes increasingly frequent due to abuse and its popularity.
To counter this, I will make the following changes:
The rate limit will decrease from 200 per hour to 50 per hour.
By January 31st, 2021, cors-anywhere.herokuapp.com will stop serving as an open proxy.
From February 1st. 2021, cors-anywhere.herokuapp.com will only serve requests after the visitor has completed a challenge: The user (developer) must visit a page at cors-anywhere.herokuapp.com to temporarily unlock the demo for their browser. This allows developers to try out the functionality, to help with deciding on self-hosting or looking for alternatives.
CORS Anywhere is a node.js proxy which adds CORS headers to the proxied request.
To use the API, just prefix the URL with the API URL. (Supports https: see github repository)
If you want to automatically enable cross-domain requests when needed, use the following snippet:
$.ajaxPrefilter( function (options) {
if (options.crossDomain && jQuery.support.cors) {
var http = (window.location.protocol === 'http:' ? 'http:' : 'https:');
options.url = http + '//cors-anywhere.herokuapp.com/' + options.url;
//options.url = "http://cors.corsproxy.io/url=" + options.url;
}
});
$.get(
'http://en.wikipedia.org/wiki/Cross-origin_resource_sharing',
function (response) {
console.log("> ", response);
$("#viewer").html(response);
});
Whatever Origin
Whatever Origin is a cross domain jsonp access. This is an open source alternative to anyorigin.com.
To fetch the data from google.com, you can use this snippet:
// It is good specify the charset you expect.
// You can use the charset you want instead of utf-8.
// See details for scriptCharset and contentType options:
// http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
$.ajaxSetup({
scriptCharset: "utf-8", //or "ISO-8859-1"
contentType: "application/json; charset=utf-8"
});
$.getJSON('http://whateverorigin.org/get?url=' +
encodeURIComponent('http://google.com') + '&callback=?',
function (data) {
console.log("> ", data);
//If the expected response is text/plain
$("#viewer").html(data.contents);
//If the expected response is JSON
//var response = $.parseJSON(data.contents);
});
CORS Proxy
CORS Proxy is a simple node.js proxy to enable CORS request for any website.
It allows javascript code on your site to access resources on other domains that would normally be blocked due to the same-origin policy.
CORS-Proxy gr2m (archived)
CORS-Proxy rmadhuram
How does it work?
CORS Proxy takes advantage of Cross-Origin Resource Sharing, which is a feature that was added along with HTML 5. Servers can specify that they want browsers to allow other websites to request resources they host. CORS Proxy is simply an HTTP Proxy that adds a header to responses saying "anyone can request this".
This is another way to achieve the goal (see www.corsproxy.com). All you have to do is strip http:// and www. from the URL being proxied, and prepend the URL with www.corsproxy.com/
$.get(
'http://www.corsproxy.com/' +
'en.wikipedia.org/wiki/Cross-origin_resource_sharing',
function (response) {
console.log("> ", response);
$("#viewer").html(response);
});
The http://www.corsproxy.com/ domain now appears to be an unsafe/suspicious site. NOT RECOMMENDED TO USE.
CORS proxy browser
Recently I found this one, it involves various security oriented Cross Origin Remote Sharing utilities. But it is a black-box with Flash as backend.
You can see it in action here: CORS proxy browser
Get the source code on GitHub: koto/cors-proxy-browser
You can use Ajax-cross-origin a jQuery plugin.
With this plugin you use jQuery.ajax() cross domain. It uses Google services to achieve this:
The AJAX Cross Origin plugin use Google Apps Script as a proxy jSON
getter where jSONP is not implemented. When you set the crossOrigin
option to true, the plugin replace the original url with the Google
Apps Script address and send it as encoded url parameter. The Google
Apps Script use Google Servers resources to get the remote data, and
return it back to the client as JSONP.
It is very simple to use:
$.ajax({
crossOrigin: true,
url: url,
success: function(data) {
console.log(data);
}
});
You can read more here:
http://www.ajax-cross-origin.com/
If the external site doesn't support JSONP or CORS, your only option is to use a proxy.
Build a script on your server that requests that content, then use jQuery ajax to hit the script on your server.
Just put this in the header of your PHP Page and it ill work without API:
header('Access-Control-Allow-Origin: *'); //allow everybody
or
header('Access-Control-Allow-Origin: http://codesheet.org'); //allow just one domain
or
$http_origin = $_SERVER['HTTP_ORIGIN']; //allow multiple domains
$allowed_domains = array(
'http://codesheet.org',
'http://stackoverflow.com'
);
if (in_array($http_origin, $allowed_domains))
{
header("Access-Control-Allow-Origin: $http_origin");
}
I'm posting this in case someone faces the same problem I am facing right now. I've got a Zebra thermal printer, equipped with the ZebraNet print server, which offers a HTML-based user interface for editing multiple settings, seeing the printer's current status, etc. I need to get the status of the printer, which is displayed in one of those html pages, offered by the ZebraNet server and, for example, alert() a message to the user in the browser. This means that I have to get that html page in Javascript first. Although the printer is within the LAN of the user's PC, that Same Origin Policy is still staying firmly in my way. I tried JSONP, but the server returns html and I haven't found a way to modify its functionality (if I could, I would have already set the magic header Access-control-allow-origin: *). So I decided to write a small console app in C#. It has to be run as Admin to work properly, otherwise it trolls :D an exception. Here is some code:
// Create a listener.
HttpListener listener = new HttpListener();
// Add the prefixes.
//foreach (string s in prefixes)
//{
// listener.Prefixes.Add(s);
//}
listener.Prefixes.Add("http://*:1234/"); // accept connections from everywhere,
//because the printer is accessible only within the LAN (no portforwarding)
listener.Start();
Console.WriteLine("Listening...");
// Note: The GetContext method blocks while waiting for a request.
HttpListenerContext context;
string urlForRequest = "";
HttpWebRequest requestForPage = null;
HttpWebResponse responseForPage = null;
string responseForPageAsString = "";
while (true)
{
context = listener.GetContext();
HttpListenerRequest request = context.Request;
urlForRequest = request.RawUrl.Substring(1, request.RawUrl.Length - 1); // remove the slash, which separates the portNumber from the arg sent
Console.WriteLine(urlForRequest);
//Request for the html page:
requestForPage = (HttpWebRequest)WebRequest.Create(urlForRequest);
responseForPage = (HttpWebResponse)requestForPage.GetResponse();
responseForPageAsString = new StreamReader(responseForPage.GetResponseStream()).ReadToEnd();
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Send back the response.
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseForPageAsString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
response.AddHeader("Access-Control-Allow-Origin", "*"); // the magic header in action ;-D
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
//listener.Stop();
All the user needs to do is run that console app as Admin. I know it is way too ... frustrating and complicated, but it is sort of a workaround to the Domain Policy problem in case you cannot modify the server in any way.
edit: from js I make a simple ajax call:
$.ajax({
type: 'POST',
url: 'http://LAN_IP:1234/http://google.com',
success: function (data) {
console.log("Success: " + data);
},
error: function (e) {
alert("Error: " + e);
console.log("Error: " + e);
}
});
The html of the requested page is returned and stored in the data variable.
To get the data form external site by passing using a local proxy as suggested by jherax you can create a php page that fetches the content for you from respective external url and than send a get request to that php page.
var req = new XMLHttpRequest();
req.open('GET', 'http://localhost/get_url_content.php',false);
if(req.status == 200) {
alert(req.responseText);
}
as a php proxy you can use https://github.com/cowboy/php-simple-proxy
Your URL doesn't work these days, but your code can be updated with this working solution:
var url = "http://saskatchewan.univ-ubs.fr:8080/SASStoredProcess/do?_username=DARTIES3-2012&_password=P#ssw0rd&_program=%2FUtilisateurs%2FDARTIES3-2012%2FMon+dossier%2Fanalyse_dc&annee=2012&ind=V&_action=execute";
url = 'https://google.com'; // TEST URL
$.get("https://images"+~~(Math.random()*33)+"-focus-opensocial.googleusercontent.com/gadgets/proxy?container=none&url=" + encodeURI(url), function(data) {
$('div.ajax-field').html(data);
});
<div class="ajax-field"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
You need CORS proxy which proxies your request from your browser to requested service with appropriate CORS headers. List of such services are in code snippet below. You can also run provided code snippet to see ping to such services from your location.
$('li').each(function() {
var self = this;
ping($(this).text()).then(function(delta) {
console.log($(self).text(), delta, ' ms');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/jdfreder/pingjs/c2190a3649759f2bd8569a72ae2b597b2546c871/ping.js"></script>
<ul>
<li>https://crossorigin.me/</li>
<li>https://cors-anywhere.herokuapp.com/</li>
<li>http://cors.io/</li>
<li>https://cors.5apps.com/?uri=</li>
<li>http://whateverorigin.org/get?url=</li>
<li>https://anyorigin.com/get?url=</li>
<li>http://corsproxy.nodester.com/?src=</li>
<li>https://jsonp.afeld.me/?url=</li>
<li>http://benalman.com/code/projects/php-simple-proxy/ba-simple-proxy.php?url=</li>
</ul>
Figured it out.
Used this instead.
$('.div_class').load('http://en.wikipedia.org/wiki/Cross-origin_resource_sharing #toctitle');

How to force jQuery to issue GET request to server to refresh cached resource

There are a lot of answers all around the web how to prevent jQuery AJAX requests from being cached. This is not what I'm trying to do.
I need the AJAX request to be returned from the cache based on condition in my code. That is, when the request is issued during page load, it is completely ok to return cached response (in fact that is the intention). But when the user clicks a button, I need the same request to be refreshed from the server.
I know that I can do this by setting cache: false option in jQuery, but this will just request the data with a different URL thus not refreshing the response in the cache and during the next page load the request with cache: true will return the obsolete data.
I tried adding a Cache-Control header to the request but that does not have effect at least in IE10.
$.ajax({
url: Configuration.ApiRootUri + "Notifications",
type: "GET",
headers: { 'Cache-Control': refreshCache ? 'no-cache' : 'private' },
success: function(data) {}
});
Is it possible at all to force the browser to refresh a cached resource from script?
(note: rewritten after discussion in the comments)
A solution would be to store the response from the server in the localstorage and load it at page load if it does exists.
This avoid completely the request made to the server at start and act like a cache, but in the browser.
Here's a pseudo-algo example :
function loadRequest(){
Call Ajax request
On success, store the result in the localstorage, call updateHtml(result);
}
function updateHtml(data) {
Update the specific html (list, view, etc) with the data
}
$('#mybutton').on ('click', loadRequest);
$(function() {
Check if the data in the localstorage exists
if exists, call updateHtml(localstorage.value);
else call loadRequest
});
That's it!

Simple way to monitor result of request to detect session timeout?

I've got an Ajax app I would like to add sessions and logins to.
Currently the app uses jQuery get shorthand to send a request
that.ajaxRequest = $.get(query_string, function(data, textStatus, xhr) {
// do somthing with data
});
I have a system on the server that throws an error message if the session has expired.
On the client I know I can deal with this using the following:
that.ajaxRequest = $.get(query_string, function(data, textStatus, xhr) {
if (data == "E_TIMEOUT") {
// redirect to login
} else {
// do something
}
});
However I would prefer not to have to write the same timeout code for every $.get - there are a bunch of them throughout the app.
Is there a way to monitor the result of a request and take action if necessary, without rewriting all of my $.get statements?
Thanks,
You can use $.ajaxSetup() for this:
$.ajaxSetup({
complete: function (xhr) {
if (xhr.getResponseHeader('E_TIMEOUT') == "1") {
//redirect to login
}
}
});
This approach is slightly different, it uses response headers which you can get without parsing data an extra time every request (that is possible, though not documented and subject to break), which you would have to do otherwise, in a global situation.
On the server-side where you're sending E_TIMEOUT for the response, just change it to add a header as well, E_TIMEOUT=1 (or whatever header you want). I actually put this header on my login page only, the timeouts redirect via a 302 (which XmlHttpRequest transparently follows), so if the user timeouts and the AJAX request actually ended up at the login page, I do a location.reload() to reflect this on the page. That's just another option if you go that route, it looks like this:
$.ajaxSetup({
complete: function (xhr) {
if (xhr.getResponseHeader('LoginPage') == "1") {
location.reload();
}
}
});
Without knowing what server platform you're using I can't say how to set headers, but it's pretty easy on every common platform.
You should use firefox and download a program called 'FireBug'. This will show you all the requests being sent out, and the response that comes back from the AJAX call. It is action packed with goodness & web dev tools.

How to use JSONP to overcome XSS issue?

I have a piece of javascript executing on a jetty server which is sending a XMLHTTPRequest to a scoket on another server(wamp server). The request gets sent to the socket, however the XHR response seems to be getting blocked.
I have heard that I can use JSONP to overcome this problem.
However as I am new to both javascript and I have never used JSONP technique before I would greatly appreciate any help in how to use this technique?
function sendPost(url, postdata, callback) {
xmlHttp=GetXmlHttpObject()
if (xmlHttp==null) {
alert ("Browser does not support HTTP Request")
return
}
xmlHttp.onreadystatechange=callback
xmlHttp.open("POST",url,true)
xmlHttp.send(postdata);
}
function sendInitRQ(width, height) {
var post = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><command type=\"init\"><width>" + width + "</width><height>" + height + "</height></command>";
sendPost("http://localhost:80/socket.php", post, initReturned);
}
I know that the php socket is recieving the post as when i check the server log i get a 200 on the get request.
I just want to know how can I use the JSONP approach?
I have seen exampples of the approach but Iam stilll unsure of how to do it.
The JSONP technique uses a completely different mechanism for issuing HTTP requests to a server and acting on the response. It requires cooperating code in the client page and on the server. The server must have a URL that responds to HTTP "GET" requests with a block of JSON wrapped in a function call. Thus, you can't just do JSONP transactions to any old server; it must be a server that explicitly provides the functionality.
The idea is that your client-side code creates a <script> block dynamically, with the "src" attribute set to the URL of the JSONP server. The URL should contain a parameter telling the server the name of the Javascript function you expect it to call with the JSON data. (Exactly what parameter name to use depends on the server; usually it's "callback", but I've seen some that use "jsonp".) The client must of course have that function in the global scope. In other words, if you have a function like
function handleJSON(json) {
var something = json.something;
// ... whatever ...
}
then your URL tells the server to call "handleJSON", and the server response should look like this:
handleJSON({"id": 102, "something": { "more": "data", "random": true }});
Thus when the <script> block is loaded from the "src" URL you gave, the browser will interpret the contents (the response from the server) and your function will be called.
It should be clear that you should only make JSONP requests to servers you trust, since they're sending back code to execute in your client, with access to any active session(s) your client has with other secured sites.
edit — Here's a nice article: http://www.ibm.com/developerworks/library/wa-aj-jsonp1/

Categories

Resources