How to bypass authorization using XMLHttpRequest header? - javascript

I am trying to access a diagram (the type of url is image), which require authorization, and display it in fancybox. So I tried to use XMLHttpRequest to send authorization in header first and access this url again since the authorization has already bypassed in cache, that's why I set async to false. Here is my code:
function showDiagram(row){
var authCode = $("#authCode").text();
var header = "basic" + authCode;
console.log("In show diagram");
var id = row.children().eq(6).text();
openURL = restURL + "/service/runtime/process-instances/" + id + "/diagram";
console.log(openURL);
var xhr
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}
else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open("GET", openURL, false);
xhr.setRequestHeader("Authorization", "Basic " + authCode);
xhr.send();
console.log(xhr.status);
if(xhr.status==200){
callFancybox(openURL);
}else{
alert("The diagram cannot be shown!");
}
}
However, I have met two different error situations.
The browser still ask me to enter username and password when I access the url for the first time, at the same time, console print out status = 200 and fancybox has already popped up, if I refuse to enter password, a GET 401(Unauthorized) error will be returned. If I entered the password, the diagram will show in fancybox and it will work well for all other url I am trying access.
Console print out status = 200 and the fancybox will pop and show "The requested content cannot be loaded.
Please try again later." And a GET 401(Unauthorized) error will be returned.
Why this happening? What should I do to bypass auto login and display the url in fancybox? Thank you very much for any ideas!

You're not specifying what is on the server side, and the problem is there. You must modify your server so that it supports your header authentication. With your current implementation the server is ignoring the headers, and so it's challenging you to indentify in a different way.
Besides you'r not sending any kind of credentials or whatever. You must add headers with that information.

Related

Django POST Response throws exception if page is refreshed/reopened

I'm using Django in order to create a server which will download and track the progress of files. It has an input field for the url of the download link, and once I click on Download, it executes the following javascript code:
index.html
function startDownload() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
completedFile = xhttp.responseText;
document.getElementById(completedFile + "ProgBar").style.width = "100%";
document.getElementById(completedFile + "ProgBar").innerHTML = "Completed!";
setTimeout(delProgBar, 5000, completedFile);
}
};
xhttp.open("POST", "http://localhost:8000/download/start/", true);
xhttp.setRequestHeader("X-CSRFToken", '{{ csrf_token }}');
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
var downloadLink = document.getElementById("downloadInput").value;
var sendString = "url=" + downloadLink;
var downloadArray = downloadLink.split('/');
var downloadName = downloadArray[downloadArray.length-1];
addProgBar(downloadName, "0");
xhttp.send(sendString);
}
Django views.py
def start(request):
aux = request.POST['url']
file_name = start_download(aux)
print("###########")
print(file_name)
return HttpResponse(file_name)
This works completely fine IF I don't reload the page after starting the download. The POST Request is only being logged on Django Command Prompt window after it is completed (readyState check).
However, the page should show the download progress for other people that open the page after the download started. This however is throwing a big error on Django, with something like 4 Exceptions. I can post it later if necessary, but the main one seems to be:
ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine
I believe that what I'm doing wrong is trying to send a response to a client that did not generated the request.
Is there a way to receive the POST Response even if the client did not sent the original Request? Or is there another way to do what I'm trying to?
Thanks a lot!
Additional Info:
Tried with GET instead of POST and the same issue happens.
I believe that the main issue here is that the POST Request is only returning an answer to the client once the operation "start_download" finishes, which can take a while. I should instead send a response with "success" or "fail" to start the download and use GET Request to perform polling and get the download status.
Firstly it looks like the code checks for POST values.
To fix this check if the request method is POST or GET. If its GET then you need to render to a page that displays the progress of the files.

Why does this email sending function not work?

Heres my email sending function:
function send() {
var key = "dJdJekCVAFIqvUJ13DEczZjgIh_4MyeIGEHz2GBYKFe";
var message_name = "defender_send_message";
var data = {};
data.value1 = document.getElementById('textBox').value;
data.value2 = localStorage.getItem("AdminsEmail");
var url = "https://maker.ifttt.com/trigger/" + message_name + "/with/key/" + key;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status == 200) {
console.log("Message Sent");
}
}
}
xmlhttp.open('POST', url, true);
xmlhttp.responseType = 'json';
xmlhttp.send(new FormData(data));
}
I wanted to create an email sending function with only pure js, not jquery or anything. I get the following errors when i click send:
(ignore the first error i fixed that already)
I had a jquery function that worked (but i had to get rid of it):
var message = localStorage.getItem("Message");
console.log(message + localStorage.getItem("AdminsEmail"));
var key = "dJdJekCVAFIqvUJ13DEczZjgIh_4MyeIGEHz2GBYKFe"; // << YOUR KEY HERE
var message_name = "defender_send_message"; // << YOUR MESSAGE NAME HERE
var url = "https://maker.ifttt.com/trigger/" + message_name + "/with/key/" + key;
$.ajax({
url: url,
data: {value1: message,
value2: localStorage.getItem("AdminsEmail")},
dataType: "jsonp",
complete: function(jqXHR, textStatus) {
console.log("Message Sent");
}
});
why would this work and my other function not?
EDIT 2 : Since it seems the endpoint doesn't actually return JSON, I think your original jQuery code wasn't correct either. You need to do more research into this iftt.com platform and how to use it. From what I can tell, it's meant to be used in a mobile app, not in the browser- it would be a normal POST XHR then, and CORS doesn't apply to mobile apps. They have this page for testing the endpoint- notice that it gives you an example using curl, a command-line tool, where again CORS doesn't apply. So I think you need to rethink things, this service is not designed to be used from a browser, like you are trying to do.
EDIT: since it turns out you are actually trying to use JSONP and not a plain XHR, all you need to do is implement that without jQuery- create a script tag with the server's URL and add a URL parameter to define your callback function to handle the data. This answer should give you the solution.
In your case the code might look like this :
http://www.codeply.com/go/bp/VRCwId81Vr
function foo(data)
{
// do stuff with JSON
console.log(data)
}
var script = document.createElement('script');
script.src = "https://maker.ifttt.com/trigger/defender_send_message/with/key/"+
"dJdJekCVAFIqvUJ13DEczZjgIh_4MyeIGEHz2GBYKFe?callback=foo";
document.getElementsByTagName('head')[0].appendChild(script);
Note that this doesn't work for me(but with your code, you would get Message sent printed to the console, so maybe you thought it was working?)- the response isn't JSON. Most likely the endpoint isn't actually meant to be used for JSONP?
My answer below only applies if you are trying to do a regular XHR in a browser without JSONP.
This happens because of the Cross Origin Resource Sharing policy of your browser. Your code is hosted at localhost, and it is trying to access a resource hosted at maker.ifttt.com through an XmlHttpRequest. In order to allow this to happen, the server at maker.ifttt.com would need to be configured to allow access from the localhost origin. Presumably you can not make that change as you don't control that server.
In your case, the best solution would be to make the request to maker.ifttt.com through your own server- CORS doesn't apply for server-to-server requests. Send the XmlHttpRequest to your server, take the data regarding the email from the request URL parameters, and then make the request to maker.ifttt.com using that data.

Page Redirect and set request custom request header

I am looking for a way to redirect to another web app and set a custom request header. This is manly because the web app that I am going to is expecting a custom header for the user name call "REMOTE_USER" where I will set the user name to.
I was exploring the use of a java servlet or maybe writing some java script to do this.
var client = new XMLHttpRequest();
client.open("POST", "/log");
client.setRequestHeader("REMOTE_USER", "User1");
window.location.href = "http://myserver.com:8080/webapp/";
I am seeing that the page is getting redirected but don't see any custom request headers
function customHeader(remoteinput, userinput) {
var client = new XMLHttpRequest();
client.open("POST", "/log");
client.setRequestHeader(remoteinput, userinput);
window.location.href = "http://myserver.com:8080/webapp/";
}
Now execute your function:
customHeader("something", "user42");
If I am misinterpreting your question, please reply!

Ajax request: Refused to set unsafe header

I am trying to play an audio using Google Text-To-Speech. Therefore I need to post a request to their endpoint with the Referer and the User-Agent properly set. This call should return an MP3 that I can play.
However, I get Refused to set unsafe header errors. This is my code. How can I do this?
$.ajax({
url: 'http://translate.google.com/translate_tts?ie=UTF-8&q=Hello&tl=en&client=t',
beforeSend: function(xhr) {
xhr.setRequestHeader("Referer", "http://translate.google.com/");
xhr.setRequestHeader("User-Agent", "stagefright/1.2 (Linux;Android 5.0)");
}, success: function(data){
el.mp3 = new Audio(data);
el.mp3.play();
}
});
You can't. It is impossible.
The specification requires that the browser abort the setRequestHeader method if you try to set the Referer header (it used to be that User-Agent was also forbidden but that has changed)..
If you need to set Referer manually then you'll need to make the request from your server and not your visitor's browser.
(That said, if you need to be deceptive about the user agent or referer then you are probably trying to use the service in a fashion that the owner of it does not want, so you should respect that and stop trying).
Note that while jQuery wraps XHR, the same rules apply to fetch.
Empty Origin and Referer headers with GET XMLHttpRequest from <iframe>
Well actually, it is possible; at least for ordinary web pages.
The trick consists in injecting an XMLHttpRequest
function into an empty <iframe>.
The origin of an empty <iframe> happens to be about://blank, which results in empty Origin and Referer HTTP headers.
HTML:
<iframe id="iframe"></iframe>
JavaScript:
const iframe = document.getElementById('iframe');
const iframeWin = iframe.contentWindow || iframe;
const iframeDoc = iframe.contentDocument || iframeWin.document;
let script = iframeDoc.createElement('SCRIPT');
script.append(`function sendWithoutOrigin(url) {
var request = new XMLHttpRequest();
request.open('GET', url);
request.onreadystatechange = function() {
if(request.readyState === XMLHttpRequest.DONE) {
if(request.status === 200) {
console.log('GET succeeded.');
}
else {
console.warn('GET failed.');
}
}
}
request.send();
}`);
iframeDoc.documentElement.appendChild(script);
JavaScript evocation:
var url = 'https://api.serivce.net/';
url += '?api_key=' + api_write_key;
url += '&field1=' + value;
iframeWin.sendWithoutOrigin(url);
Having the possibility of sending empty Origin and Referer HTTP headers is important to safeguard privacy when using third-party API services. There are instances where the originating domain name may reveal sensitive personal information; like being suggestive of a certain medical condition for example. Think in terms of https://hypochondriasis-support.org :-D
The code was tested by inspecting the requests in a .har file, saved from the Network tab in the F12 Developer View in Vivaldi.
No attempt in setting the User-Agent header was made. Please, comment if this also works.
There are some header, which browser doesn't allow programmer to set its value in any of the javascript framework (like jQuery, Angular, etc.) or XMLHttpRequest ; while making AJAX request. These are called the forbidden headers: Forbidden Header

iOS: Authentication using XMLHttpRequest - Handling 401 response

I'm writing an iOS application using PhoneGap (aka Cordova), I have a simple html login page that logs the user in using an XMLHttpRequest with basic authentication over SSL. Everything works splendidly when you enter your username and password correctly. However, if you enter the wrong username/password none of my callbacks are ever called.
If you run the same code on Chrome for example, with the wrong username/password, chrome behaves in a similar manner, except it pops up an authentication challenge dialog. Hitting cancel on chrome's dialog returns control to my javascript code. Unfortunately, on iOS, the UIWebView wont even popup an auth dialog, it just hangs. I need a way to tell the user that they entered the wrong username or password so they can retry.
The closest thing to an answer I could find was this http://www.freelock.com/2008/06/technical-note-http-auth-with-ajax but changing the response status from the server doesn't seem like the right thing to do.
Here's basically what my request code looks like, but when a bad username or password is sent it never reaches my onload callback (in fact the onreadystatechange callback only gets called once and thats for readyState 1, aka OPEN).
var req = new XMLHttpRequest();
req.onload = function(ev) {
if (req.status == 401) {
alert("Invalid Username/Password");
document.getElementById('password').focus();
} else if (req.status == 200) {
window.location.href = some_secure_site;
} else {
// edit //
alert("Some other status");
}
}
req.onerror = function (ev) { alert('Error'); };
req.ontimeout = function(ev) { alert('Timeout'); };
req.open('GET', uri, true, userValue, passValue);
req.withCredentials = true;
req.send();
A few things became apparent to me while trying to do this on iOS. One is that iOS has a bug relating to basic auth, so if your password has certain special characters in it you'll never get a response back from your server because your server will never get an authentication challenge. That is, if you're using the username and password field in the "open" method.
My guess is they are doing something stupid like sending it via http://username:password#myorigin.com/etc when they should be using http headers and base64 encoding the creds like so
req.setRequestHeader("Authorization", "Basic " + base64(username) + ':' + base64(password));
The other thing I learned is that Basic Auth isnt very secure and is prone to a million and one problems. One of which that will annoy you is that the client will cache the username and password, which will override any new values you send via "req.open(...)". Good luck getting around that using javascript alone, you'll have to do some magic in ObjC to clear the cache.
If you have control over your server, I would suggest using token authentication. Connect over SSL and then send a POST with JSON data containing the username and password. The server could then send back JSON data with an authentication token (essentially a bunch of random characters long enough that it can't ever be guessed, a UUID works well. this is generated by the server and can only be known to the client and the server). Then store the token and the username in the keychain so the user doesnt need to enter their creds everytime they start your app.
My server will always send back a 200 response but the JSON data will contain the information needed to either retry or to store the auth token. In general... basic auth basically sucks.
try {
var req = new XMLHttpRequest();
req.onload = function(ev) {
var response = JSON.parse(this.responseText);
if (response.success === true) {
// The server will respond with a token that will allow us to login
storeCredentials(userValue, response.token);
// redirect with token
else if (req.status == 401) {
alert("Invalid Username/Password");
document.getElementById('password').focus();
} else {
alert("Some other status");
}
}
req.ontimeout = setTimeout(function(ev) { navigator.notification.alert('Timeout trying to contact the server'); }, 10000);
req.onerror = function(ev) { clearTimeout(this.ontimeout); navigator.notification.alert('Error connecting to the server during authentication.'); };
var uri = myWebOrigin + '/authenticate';
req.open('POST', uri, true);
req.setRequestHeader('Cache-Control', 'no-cache');
req.setRequestHeader('Content-Type', 'application/json');
json_data = {username : Base64.encode(userValue), password : Base64.encode(passValue)};
req.send(JSON.stringify(json_data));
} catch(error) {
navigator.notification.alert('Uh oh, an error occurred trying to login! ' + error);
return;
}
I just had the same issues with none of the callbacks being called when using iOS + PhoneGap + jQuery. If I pass incorrect credentials and use
$.ajax({
...
timeout: 5000, // Some timeout value that makes sense
...
});
then the error callback is called with {"readyState":0,"status":0,"statusText":"timeout"}. In that case you would have to guess that the real error is the HTTP 401.
Alternatively you can use
$.ajax({
...
async: false, // :-(
...
});
and your error callback will get something like {"readyState":4,"responseText":"<html>...</html>","status":401,"statusText":"Unauthorized"} back.
To solved this problem remove the header WWW-Authenticate from server response.
There is probably an other HTTP status code beside the 401 and 200 codes received! Make sure there is really no other status code received:
if (req.status == 401) {
alert("Invalid Username/Password");
document.getElementById('password').focus();
} else if (req.status == 200) {
window.location.href = some_secure_site;
} else {
alert('Unfetched status code '+req.status+' captured!');
}
I've been having the same issue - none of the callbacks on the request are being called if invalid credentials are passed in.
My guess is that internally the UIWebView is being told to prompt for credentials, but that prompt is being suppressed, leading to this bug. This is based on all the other browsers I've tried (Safari, Chrome, Firefox) not calling the callbacks in this case until after the prompt is dismissed.
Another solution (the one I'm using) would be to not use Javascript to do the authentication, and instead do it on the iOS side - you can use the code on How to display the Authentication Challenge in UIWebView? as a rough template for doing this.

Categories

Resources