1

I am trying to put together a simple login prompt for a bit of in house testing and I have found myself stuck. I have the login prompt made via HTML and am trying to send it off via xmlhttprequest. Here is my JS code:

var xhr = new XMLHttpRequest();

function loginResults() {
  var loginUser = document.getElementById("username").value;
  var loginPass = document.getElementById("password").value;
  //console.log(loginUser + loginPass);
  xhr.open("post", "https://test.com/api/login/");
  var loginData = "username=" + loginUser + "&password=" + loginPass
  xhr.send(loginData);
  //console.log(loginData);
  xhr.addEventListener("readystatechange", processRequest, false);
}

function processRequest(e) {
  if (xhr.readyState == 4 && xhr.status == 200) {
    var response = JSON.parse(xhr.responseText);
    console.log(response);
  }
}

The issue is that the xhr.send completely fails using this method. However if I replace the variables sent with the plain text then everything works fine. Like this:

var loginData = "username=" + "test@test.com" + "&password=" + "test1234"

What is the difference between pulling the info via the form data and having the login hard coded like that? The request payload is exactly the same in both instances.

EDIT Here is the gist of my HTML form:

    <form name="isLogin" id="isLogin" onSubmit="loginResults()" method="post">

<div class="container">

<label for="username"><b>Email</b></label>

<input type="text" id="username" placeholder="Enter Email" name="username" required>

<label for="password"><b>Password</b></label>

<input type="password" id="password" placeholder="Enter Password" name="password" required>

<button id="submitLogin"  type="submit">Login</button>

Puddin
  • 23
  • 4
  • What do you get from `console.log(loginUser + loginPass);` and `console.log(loginData);`? Also, is there any explicit error (what do you mean by it "completely fails"? – Jonathan Lam May 09 '18 at 21:57
  • 1
    what do you mean "completely fails"? What does the first `console.log()` display when you uncomment it? Right now my first guess is that the two variables don't contain what you think they do. You should also add the relevant HTML to your question. –  May 09 '18 at 21:57
  • Firstly, you would need to escape the value, instead of concatenating it directly. – Diego S. May 09 '18 at 22:01
  • If HTML is as expected, code should work perfectly fine: https://jsfiddle.net/khrismuc/tca8ot5a/ –  May 09 '18 at 22:07
  • By "completely fails" I mean when using the dev console in my web browser the login request shows as cancelled rather than a 200 or a 403 or something like that. If I uncomment out the first console log I get "test@test.comtest1234" If I uncomment the second I get "username=test@test.com&password=test1234" The results are the same if I hard code the login opr if it is given via the form data. This is why I'm confused as to why it won't work. If I had to guess I'm thinking xmlhttprequest doesn't like how I'm trying to pass the data through, – Puddin May 09 '18 at 22:09
  • @Puddin You said yourself that hardcoding the values works fine, so I don't think how you pass the data is an issue. –  May 09 '18 at 22:11
  • @Chris G I agree hence why I'm confused and stuck. The "console.log(loginData)" outputs the same regardless if I hard code it or pull from the form. Yet pulling from the from fails whilst hard coding does not. – Puddin May 09 '18 at 22:17
  • @Puddin So what does your backend look like? How is it processing the request body? –  May 09 '18 at 22:20
  • @Chris G Unfortunately I cannot answer that as thoroughly as I'd like as I do not manage the backend. This is a test to see how integration could work with this software. If you have a more specific question I can do my best to answer it. – Puddin May 09 '18 at 22:31
  • Before calling `xhr.send()`, call `xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");` so the server actually receives the parameters. However if this works, then the problem wasn't what you outlined in your question. –  May 09 '18 at 22:40
  • That gives the same issue. Request status shows as "Canceled" rather than a 200 or a 404 or a 502 via the chrome web console. What would cause it to show as cancelled rather than a status code? – Puddin May 09 '18 at 22:49
  • Look here: https://stackoverflow.com/questions/12009423/what-does-status-canceled-for-a-resource-mean-in-chrome-developer-tools Is it possible you have `loginResults()` as your `onsubmit` handler? Or as `onclick` handler of a form button? Because in that case Chrome cancels the request due to the form also being submitted the non-ajax way. Again, show the relevant HTML... –  May 09 '18 at 22:54
  • In your `
    `, change `onSubmit="loginResults()"` to `onSubmit="loginResults(); return false;"`
    –  May 09 '18 at 23:03
  • I updated my original post to include the raw form I am using. I tried using both the onSubmit and the onClick handlers to the same effect. – Puddin May 09 '18 at 23:04
  • @Puddin Did you see my previous comment? You're sending the form data via AJAX, then immediately after submit the form the usual way, causing Chrome to cancel the AJAX request (since submitting the form will load a fresh page). –  May 09 '18 at 23:07
  • No sorry I was working on responding and didn't see your post. That indeed worked. I have been muttering "wtf" to myself all week and you have finally saved me of this. I appreciate your help. – Puddin May 09 '18 at 23:15
  • Just FYI: I found the answer by googling "chrome request cancelled": https://stackoverflow.com/a/24062160/5734311 –  May 09 '18 at 23:18

2 Answers2

1

The reason the request gets cancelled is you aren't intercepting the standard form submission. When you click the Login button, Chrome fires off the AJAX request, then also submits the form. Since this leads to a page load, Chrome cancels the AJAX request.

What you need to do is prevent the default event handling. A clean way to do this is to add the submission handling in your script:

document.getElementById("isLogin").onsubmit = function(e) {
  e.preventDefault();
  // AJAX here
  console.log("form submission intercepted");
};
<form name="isLogin" id="isLogin" method="post">
  <div class="container">
    <label for="username"><b>Email</b></label>
    <input type="text" id="username" value="test@test.com" name="username" required>
    <label for="password"><b>Password</b></label>
    <input type="password" id="password" value="test1234" name="password" required>
    <button id="submitLogin" type="submit">Login</button>
  </div>
</form>
0

You need to escape values before concatenating them to the URL querystring:

    var loginUser = encodeURIComponent(document.getElementById("username").value);
    var loginPass = encodeURIComponent(document.getElementById("password").value);

Secondly, i wouldn't recommend passing password directly through querystring. The secure way to pass it is preferably salted-and-hashed.

Diego S.
  • 161
  • 1
  • 9
  • Why exactly the downvotes both on the question and on my answer? – Diego S. May 09 '18 at 22:06
  • I downvoted your answer because it doesn't address the issue. Also, OP is already using POST, which makes your answer moot. –  May 09 '18 at 22:08
  • @ChrisG I think it does address the issue, as the issue occurs when he passes the formdata via concatenating their values as property `value` into the URL, something that doesn't occur when he concatenates the values as a constant literal string. The problem seems to be caused by special characters that need to be escaped properly (for instance, the at symbol "@" should be escaped as "%40"). – Diego S. May 09 '18 at 22:13
  • But he *isn't* concatenating anything into the URL. That's my point. HE IS USING **POST**. –  May 09 '18 at 22:18
  • @ChrisG Yes, you're right, i double checked the code and noticed that. Fixed my answer, but kept the hashing suggestion (as it's yet not secure, but it's just a suggestion, not a law). – Diego S. May 09 '18 at 22:20
  • I'm not seeing any fix in your answer. It still mentions "querystring", twice. –  May 09 '18 at 22:41
  • @ChrisG URL-encoding is necessary when passing POST data as well. – Barmar May 09 '18 at 22:43
  • @DiegoS. Password hashing is usually done on the server when storing into the DB, not on the client. The password is protected in the POST data using SSL. – Barmar May 09 '18 at 22:45
  • @Barmar I wouldn't call it "necessary", which implies it won't work without. It does. –  May 09 '18 at 22:58
  • @ChrisG It's necessary if any of the parameters contain some special characters, such as `=` or `&`. – Barmar May 09 '18 at 23:08
  • @Barmar I know, I'm aware how POST data works. None of this pertains to the question at hand. –  May 09 '18 at 23:10