POST request sent twice using fetch js - javascript

I call a POST method with javascript using fetch, Checked my server logs and see these lines:
2020-02-08,14:07:21 [WARNING] (web.py:web:1618): 400 POST /login (::1): Missing argument username
2020-02-08,14:07:21 [WARNING] (web.py:web:2106): 400 POST /login (::1) 8.64ms
...
2020-02-08,14:07:21 [DEBUG] (base_handler.py:base_handler:123): Attempted Sign-in by asdas
2020-02-08,14:07:21 [INFO] (web.py:web:2106): 200 POST /login (::1) 6.07ms
Here is the js which sends the request and then uses the response to add text to the page if login is unsuccessful:
function ready(){
function pwdSubmission(){
const url = window.location.href;
var result = document.getElementById("result-text");
var username = document.getElementById("user").value;
var password = document.getElementById("pwd").value;
fetch(url, {method:"post", headers:{
"Content-Type": "application/json",
"Accept": "application/json"},
body:{"username":username, "password":password}}
).then(function(data){
if (data.status !== 200){
result.style.color = '#d9534f';
}
return data.json()
}).then(data =>
result.innerText = data["message"])
}
var postbtn = document.getElementById("post-btn");
postbtn.addEventListener("click", pwdSubmission)
}
So why is the POST sent twice and the first time it doesn't include the arguments.
UPDATE
My server have no problem grabbing the arguments from the body even without stringify but there is no body in the first request, only the second, so I get an error which sends json back

The body param isn't correct. You're passing an object when it should be a JSON string.
body:JSON.stringify({username:username, password:password}})

Figured it out. My inputs were wrapped in a form tag and the button was defaulting to a form submit so I removed those tags and it only sends once now.

Your body data type must match your "Content-Type" header ("application/json" in your case).
Using ES6 object shorthand you could do...
body: JSON.stringify({username, password})

Related

Redirecting URL With Token Parameter That Contains '+'

I'm having issues creating a redirect to an URL from an API that I'm using within my application. I request a session token which is used in the redirect URL, but I experience issues when the token contains a + in it. My guess is that the browser is picking this up as a space instead of an actual token character which is causing the redirect to fail ever so often.
Here is an example token that can come back from a request to the API: 1w5OX65MRj+3J9R5AXjMWQLAAXIo5TXa
Looking at the network tab I see that it tries to request the redirect with this token instead:
1w5OX65MRj 3J9R5AXjMWQLAAXIo5TXa, which would be why it's causing issues.
I tried replacing the + with %2B but it seems like my application isn't replacing it at all which is a little odd to me.
Here is my code:
let token = "";
$.get('/_token_req', {contentType: "application/x-www-form-urlencoded"}, (response) => {
//console.log(response);
token = response;
token = token.replace(/\+/g, "%2B"); // this doesn't replace the + character for some reason
$.get('/_redirect', {token: response}, (response) => {
//console.log(response);
if(response == "OK"){
window.location.href = "https://someapi/payments/?auth_token=" + token;
}
})
})
I don't know much about URL encoding, but if someone could point me in the right direction, that would be super helpful. Thanks!
You have 2 issues with your code:
One that you need to use encodeURIComponent to encode any symbols in your token so that it can be sent in appropriate manner.
Two, while calling the second request $.get('/_redirect', you didn't use the replaced token but the simple response that you received from earlier request.
Change your code to this to eliminate both errors:
let token = "";
$.get('/_token_req', {contentType: "application/x-www-form-urlencoded"}, (response) => {
token = encodeURIComponent(response);
$.get('/_redirect', {token: token}, (response) => {
if(response == "OK"){
window.location.href = "https://someapi/payments/?auth_token=" + token;
}
})
})

How to do otp verification using 2 factor

I have taken free trail from 2 factor for sending sms as otp and needs to be verified again, I am able to send the sms and also console.log the status on node side, but the issue is I am not able to send the response to client weather otp is sent or not and how could I verify it
What I have done till now
let otp = Math.floor(100000 + Math.random() * 900000) // geterating otp
const no = req.body.cPhoneNo //phone no from UI
console.log(no)
var options = {
"method": "POST",
"hostname": "2factor.in",
"port": null,
"path": "/API/V1/{{api_key}}/ADDON_SERVICES/SEND/TSMS",
"headers": {}
};
var req = http.request(options, function(res) {
var chunks = [];
res.on("data", function(chunk) {
chunks.push(chunk);
});
res.on("end", function() {
var body = Buffer.concat(chunks);
let sendData = body.toString()
console.log(body.toString())
res.json({status:body.toString}) //here I am getting error as type error json is not a function
});
});
req.write(JSON.stringify({
From: 'something',
To: no,
TemplateName: 'some Name',
VAR1: 'var 1',
VAR2: otp
// SendAt: '{OptionScheduleTime}'
}));
req.end();
I have mentioned with comment Where I am trying to send status back to client if it is send or not, but it is not taking json as throwing error .json throws type error
**One more thing I found from there website is **
I have found two url end points one to send sms with some session Id and other to get otp entered from user and verify that These are two urls
To send Otp https://2factor.in/API/V1/{api_key}/SMS/{phone_number}/AUTOGEN
To receive Otp https://2factor.in/API/V1/{api_key}/SMS/VERIFY/{session_id}/{otp_input}
api_key= The key I have got from 2factor
phone_number = receivers no
My issue is How I can use this endpoints to send the sms and to do verification, from client end I am on button click I am sending req to server via axios but in backend I have been suffering to send the msg and verify the otp
You can check out this link
Anyone out here please guide me
I don't see res.json method in node's HTTP module. Express has res.json method. Instead you should use JSON.parse. Have a look at this example from documentation.

jQuery's .ajaxSend: can't add data dynamically

I use $(document).ajaxSend(...) to dynamically add data (POST args) to some Ajax requests, when necessary.
Adding data works when some datas have been defined in an $.ajax(...) call. But if no data has been defined in the $.ajax setting, my $.ajaxSend can't add data to the settings.
Here is my $.ajaxSend interceptor:
$(document).ajaxSend(function(e, request, settings) {
if(settings.csrfIntention) {
var requestToken = {
intention: settings.csrfIntention,
hash: self.tokens[settings.csrfIntention]
}
if(!settings.data) {
settings.data = '_token=' + encodeURIComponent(JSON.stringify(requestToken));
}
else if(typeof(settings.data) == 'string') {
settings.data += '&_token=' + encodeURIComponent(JSON.stringify(requestToken));
}
else if(!settings.data._token) {
settings.data._token = requestToken;
}
}
});
And an example of $.ajax call that works:
$.ajax({
url: opts.close.url,
method: 'POST',
data: { foo:'bar' },
csrfIntention: 'ajax_close_ticket',
success: function(data) { ... }
});
The $.ajaxSend works and settings.data is set to:
foo=bar&_token=%7B%22intention%22%3A%22ajax_close_ticket%22%2C%22hash%22%3A%22uXV1AeZwm-bZL3KlYER-Dowzzd1QmCmaT6aJFjWLpLY%22%7D
Serverside, I can retrieve the two fields: foo and _token.
Now, if I remove the data object in the $.ajax call, the output of $.ajaxSend seems Ok, too:
_token=%7B%22intention%22%3A%22ajax_close_ticket%22%2C%22hash%22%3A%225cK2WIegwI6u8K_FrxywuauWOo79xvhIcASQrZ9QPZQ%22%7D
Yet, the server don't receive my _token field :(
Another interesting fact: when I have the two fields, the Chromium dev tools under the Network tab displays the two fields under a "Form Data" title. When _token is alone, "Form Data" is replaced by "Request Payload".
Edit: just understood why _token is not interpreted by the server. If no data has been previously set in $.ajax call, jQuery does not add the right Content-Type in HTTP headers, it set it to text/plain instead of application/x-www-form-urlencoded. How can I force jQuery to add this header?
Edit 2: Solution found. Will post an answer to help other people...
I apologize for my bad English
I believe that the solution to his case would be the use of the function "ajaxSetup"
var requestToken = {
intention: settings.csrfIntention,
hash: self.tokens[settings.csrfIntention]
};
$.ajaxSetup({
data : {
_token: requestToken
}
});
this way all your orders took the data as the desire
A simpler approach:
var requestToken = {
intention: settings.csrfIntention,
hash: self.tokens[settings.csrfIntention]
}
var myForm = {};
myForm['_token'] = encodeURIComponent(JSON.stringify(requestToken));
In your ajax call:
myForm['foo'] = 'bar';
$.ajax({
url: opts.close.url,
method: 'POST',
data: myForm,
csrfIntention: 'ajax_close_ticket',
success: function(data) { ... }
});
//This sends foo and _token
Finally I answer my question after only 10 minutes...
In HTTP, the form arguments are sent into the request body, under the headers. The header Content-Type should be present to declare the data type. When this header is set to application/x-www-form-urlencoded, the server will understand that you sent a form and will parse the request body as a GET URL (you know, the foo=bar&key=val format).
jQuery adds this header automatically when you set a data object into the $.ajax call. Then it passes the request to the $.ajaxSend callback, which adds its proper fields.
When no data has been provided in request settings, jQuery do not add an unnecessary Content-Type: x-www-form-urlencoded in the request headers. Then, when you append the request body into your $.ajaxSend callback, jQuery do not check the data again and declares the content as text/plain. The server has nothing to do with text/plain so it does not interpret the body data as form fields.
You can obviously force jQuery to change the header to application/x-www-form-urlencoded, let's take the code of my original post :
if(!settings.data) {
settings.data = '_token=' + encodeURIComponent(JSON.stringify(requestToken));
}
Now add the right header:
if(!settings.data) {
settings.data = '_token=' + encodeURIComponent(JSON.stringify(requestToken));
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}

Lync UCWA - Create application gives a HTTP 409: Conflict error

I have been trying for the past couple of days to develop an application for our Lync service at work using the UCWA API from Microsoft (a REST API). To get an application working: I first have to submit it to the API using a POST request to a certain URL. First, I have to authenticate with the server, and I do that by posting a username and a password credential to the API. I then get an access token back which I can use to make further requests to the API by posting the token inside the header of each request. I have been able to get an access token working, but when I try to register the application by posting a HTTP request to https://lyncextws.company.com/ucwa/oauth/v1/applications: Things will start to go wrong.
All this is done through one JavaScript file working with iframes to bypass the Same-origin policy.
This is what my code currently looks like:
<!DOCTYPE html>
<html lang="no">
<head>
<meta charset="UTF-8" />
<title>PresInfoDisp</title>
</head>
<body>
<iframe src="https://lyncextws.company.com/Autodiscover/XFrame/XFrame.html" id="xFrame" style="display: none;"></iframe>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
var access_token;
var stage = 0;
// CONNECT AND AUTHENTICATE WITH LYNC UCWA SERVICE
function connectAndAuthenticate() {
stage = 1;
var request = {
accepts: 'application/json',
type: 'POST',
url: 'https://lyncextws.company.com/WebTicket/oauthtoken',
data: 'grant_type=password&username=alexander#domain.company.com&password=somePassword'
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/WebTicket/oauthtoken');
}
// REQUEST A USER RESOURCE
function getUserResourceAuthRequest() {
stage = 0;
var request = {
accepts: 'application/json',
type: 'GET',
url: 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com'
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com');
}
function getUserResource() {
stage = 2;
var request = {
accepts: 'application/json',
type: 'GET',
url: 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com',
headers: {Authorization: "Bearer "+access_token}
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com');
}
// REGISTER APPLICATION RESOURCE
function registerApplication() {
stage = 3;
var request = {
accepts: 'application/json',
type: 'POST',
url: 'https://lyncextws.company.com/ucwa/oauth/v1/applications',
headers: {Authorization: "Bearer "+access_token},
data: {'userAgent': 'InfoDisp1', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/ucwa/oauth/v1/applications');
}
// GRAB A LIST OF CONTACTS
function listContacts() {
stage = 4;
var request = {
accepts: 'application/json',
type: 'GET',
url: 'https://lyncextws.company.com/ucwa/oauth/v1/applications',
headers: {Authorization: "Bearer "+access_token}
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/ucwa/v1/applications');
}
this.receiveMessage = function(message) {
switch(stage) {
case 1:
var beforeReplace = message.data.replace("/\\/g", "");
var json = jQuery.parseJSON(beforeReplace);
var json2 = jQuery.parseJSON(json.responseText);
access_token = json2.access_token;
console.log(json2.access_token);
console.log(message);
getUserResource();
break;
case 0:
console.log(message);
connectAndAuthenticate();
break;
case 2:
var beforeReplace = message.data.replace("/\\/g", "");
var json = jQuery.parseJSON(beforeReplace);
var json2 = jQuery.parseJSON(json.responseText);
console.log(json2._links.applications.href);
window.setTimeout(function(){registerApplication()}, 5000);
break;
case 3:
console.log(message);
break;
case 4:
break;
}
};
window.addEventListener('message', this.receiveMessage, false);
$(window).load(function() {
getUserResourceAuthRequest();
//console.log(access_token);
});
</script>
</body>
</html>
When I run this code: The last ajax query returns the error 409: Conflict, when it should be returning 201: Created
This is what my browser (Google Chrome) outputs:
The 401: Unauthorized error is supposed to happen, but the 409 Conflict, should not happen. So here is my question:
Can anyone spot why I get this 409 error instead of the 201 I should be getting?
The example code from Microsoft seems to work fine, but I want to avoid using that as it will take me a very long time to familiarize myself with it.
If there is missing data you need to spot the issue: Let me know in the comments, and i'll provide it!
If you replace
data: {'userAgent': 'InfoDisp1', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}
with a string of that data instead I.E.
data: "{'userAgent': 'InfoDisp1', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}"
it seems that data expects a string and in your example you are passing it a JSON object. Doing that makes your example work for me.
The problem seems to be with your static endpointId.
In their original helper libraries they have a method called generateUUID() which is in GeneralHelper. The best idea would be to use that method, however, if you feel like creating ayour own, go for it. The main point is that each of your application must have different endpointId.
Are you omitting the autodiscovery process for brevity only, or are you really skipping the autodiscovery in your code and assuming the URI where to post the 'create application'?
It seems more the second case to me, and this isn't right:
the URI where to create the application needs to be retrieved from the response of the user resource request (within getUserResource in the code you posted).
You have a link called applications there; its value contains the correct URI where to create the application.
http://ucwa.lync.com/documentation/KeyTasks-CreateApplication
P.S. I post here as well about the endpointId, seen I can't comment above
It is allowed to use the same application's endpointId on different clients. It is absolutely not to be assumed anyway that applications on different clients using the same endpointId will result in the same base application resource URI
I was getting this same problem using curl to experiment with this API, and failed at this same point until I figured out that in that case I needed to set content-type header to json:
curl -v --data "{'userAgent': 'test', 'endpointId': '54321', 'culture':'en-US', 'instanceID':'testinstance'}" --header 'Content-Type: application/json' --header 'Authorization: Bearer cwt=AAEBHAEFAAAAAAA..' 'https://lyncserver.com/ucwa/oauth/v1/applications'
That did the trick!

NodeJS https POST request throws socket hang up error if any data is written

I am trying to interface with an external API and I need to POST an XML document over HTTPS.
So I am using the node https interface to try to make the request but if I try to write any data (The XML document) it throws a socket hang up. If I write nothing or an empty string to the request it completes the post just fine.
I've googled and found other people with this error but I haven't been able to fix it following the solutions others have found.
I am using Meteor, which has the HTTP package for making these types of requests. The HTTP package was also throwing this error so I dug down and implemented the post using the node 'https' package thinking it would solve the issue but I get the same error with https as well.
Here is my code:
var http = Npm.require("http");
var oauthSignature = Npm.require('oauth-signature');
var URL = Npm.require('url');
var content = "XML String Here";
var postDestinationUrl = "https://example.com/api/path";
var authObject = {
oauth_consumer_key: "consumerKey",
oauth_signature_method: "HMAC-SHA1",
oauth_timestamp: (Math.round(new Date().getTime() / 1000)).toString(10),
oauth_nonce: Random.id(),
oauth_version: "1.0"
};
authObject.oauth_signature = oauthSignature.generate("POST", postDestinationUrl, authObject, "shared Secret Here");
var authString = objectToQueryString(authObject);
var headers = {
Connection: "keep-alive",
'Content-Length': Buffer.byteLength(content),
Authorization: authString,
'Content-Type': 'application/xml'
};
var parsedUrl = URL.parse(postDestinationUrl);
var requestOptions = {
hostname: parsedUrl.hostname,
path: parsedUrl.pathname,
method: "POST",
headers: headers
};
var request = https.request(requestOptions, function(response){
var body = '';
console.log("statusCode from https.request: ", response.statusCode);
console.log("headers from https.request: ", response.headers);
response.on("data", function(data){
body += data;
}); // end of data
response.on("end", function(){
console.log("body from https.request: ", body);
}); // end of end
response.on("error", function(){
console.log("error in https.request response");
}); // end of error
}); // end of request
request.write(content);
request.end();
request.on("error",function(error){
console.log("Error in https.request: " + error.message);
callback(error, undefined);
}); // end of error
var objectToQueryString = function(queryObject){
var queryString = "";
_.each(queryObject, function(value, key, list){
queryString += key + '="' + value +'",';
});
return queryString.substr(0, queryString.length -1);
};
And the error I am seeing:
"stack":"Error: socket hang up
at Object.Future.wait (/Users/dsyko/.meteor/tools/f3947a4651/lib/node_modules/fibers/future.js:326:15)
at packages/meteor/helpers.js:111
at Meteor.methods.submitAPIPost (packages/api-interface/api_server.js:412)
at packages/check/match.js:77
at _.extend.withValue (packages/meteor/dynamics_nodejs.js:35)
at Object.Match._failIfArgumentsAreNotAllChecked (packages/check/match.js:76)
at maybeAuditArgumentChecks (packages/livedata/livedata_server.js:1403)
at packages/livedata/livedata_server.js:580
at _.extend.withValue (packages/meteor/dynamics_nodejs.js:35)
at packages/livedata/livedata_server.js:579
- - - - -
at createHangUpError (http.js:1472:15)
at CleartextStream.socketCloseListener (http.js:1522:23)
at CleartextStream.EventEmitter.emit (events.js:117:20)
at tls.js:696:10
at process._tickCallback (node.js:415:13)"
I've used postman to make the same API request and it goes through just fine so it doesn't seem to be a bad endpoint. I also switched over to posting to http and used Wireshark to inspect the headers and content of the HTTP POSTs to make sure I'm not mangling something in the request but it all looked ok there. Also when I switched over to http the XML document goes through just fine and I don't see the socket hangup (Although the endpoint responds with a re-direct to the https url so I can't just use http)

Categories