HTTPS (SSL) works on firefox but not smartphone app - javascript

I'm developing an application with phonegap and Ionic and having difficulties with HTTPS (SSL). I'm puzzled over why this refuses to work.
It works like a charm on Firefox but when I'm running it on my phone it doesn't work.
If I use normal HTTP it works fine but HTTPS doesn't, I assume it got something to do with the port 443 used for SSL but no idea how to check it on my smartphone (android).
Question:
Why does HTTPS not work on my smartphone and how do I get it to work?
Login code:
$scope.login = function(name, password) {
$scope.type = 'login';
// Data to be sent to the database
var data = JSON.stringify({
type: $scope.type,
name: name.toLowerCase(),
password: password
});
var useSSL = false;
// Call httpPost from app.js with this scope and the data to be inserted
$scope.httpPost($scope, data, $scope.type, useSSL);
};
Function: httpPost
$rootScope.httpPost = function(scope, question, type, SSL) {
var urlBase = "http";
if (SSL) urlBase = "https";
var request = $http({
method: "post",
url: urlBase+"://mydomain.xyz/myproject/api.php",
crossDomain : true,
data: question,
headers: { 'Content-Type': 'application/json' }
});
/* Successful HTTP post request or not */
request.success(function(data) {
if (type == "login"){
// Confirm it's a valid token
if(data.length == 10){
showAlert('confirmation', 'you are now online');
}
else{
showAlert('Try again', 'wrong credentials');
}
}
})
.catch(function(data, status, headers, config) {
// No response from server
// This is what triggers and the data returned is useless
showAlert('Ooops', JSON.stringify(data));
});
}
Serverside
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: X-PINGOTHER');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Retrieve the incoming data
$postdata = file_get_contents("php://input");
// Check if the data is defined
if (isset($postdata)) {
// Parse the incoming data
$request = json_decode($postdata);
// Connect to database
include 'connect.php';
$db = Database::getInstance();
$db->connect();
// Login attempt and non-empty parameters
if ($request->type == "login" && $request->name != "" && $request->password != "") {
// Verify if the password matches
// Set token
// Execute the SQL
// Return the token
}
echo "Missing parameters!";
}
}
?>
If you despite all, want to see what is returned.. I have an example here (different operation, this is a delete operation instead of login but still the same result)
Edit:
Just censored some links.. apparently someone from France tried to access a non-existing folder on my server -.-

Looks like you are using a self-signed cert:
https://www.ssllabs.com/ssltest/analyze.html?d=ekstroms.xyz&latest
I presume you accepted this in Firefox and/or imported it into the Firefox trust store but you have not done this on your phone.

You have to configure your server to send the intermediate certificate.
Most probably you haven't specified 'SSLCertificateChainFile` and your phone can not recognize/validate your certificate.
Note: If You use self signed certificate then it will not be accepted automatically, You have to install it manually if possible!
If You need SSL certificate for private/non-commercial use, then You can get it from here

Related

POST request sent twice using fetch js

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})

Unable to redirect in express with Node JS

I'm learning the basics of Node.js + MongoDB and some other related tools, but I'm stuck in a simple login screen I made, the goal is, once the right credentials are given, to redirect to index.html which only has a text "welcome!". I've taken a look at different places (official API, tutorials, this page, etc.) but can't see my error yet.
This is my server:
var http = require('http');
var hostname = 'localhost';
var port = 3000;
var mongojs = require("mongojs");
var express = require("express");
const HOME_URL = "/";
const LOGIN_URL = "/login";
const LOGIN_ACTION_URL = "/doLogin";
const INDEX_URL = "/index";
var app = express();
var db = mongojs("gus:1234#127.0.0.1:27017/awesomeapp");
app.set("views", __dirname + "/public/views");
app.engine('html', require('ejs').renderFile);
app.get(HOME_URL, function(request, response) {
response.redirect(LOGIN_URL);
});
app.get(LOGIN_URL, function(request, response) {
response.render("login.html");
});
app.get(INDEX_URL, function(request, response) {
response.render("index.html");
});
app.get(LOGIN_ACTION_URL, function(request, response) {
db.users.find({
user: request.query.user,
pass: request.query.pass
}, function(err, doc) {
if(err) {
console.log(JSON.stringify(err));
response.end("An error ocurred");
return;
}
if(doc.length == 0) {
console.log("invalid_credentials");
response.end("invalid_credentials");
return;
}
console.log("user found: " + JSON.stringify(doc));
// This is my problem:
response.redirect(INDEX_URL);
});
});
app.listen(port);
and this is done in the login view:
$.ajax({
url: "/doLogin",
type: "GET",
data: {
user: $("#user").val().trim(),
pass: $("#pass").val()
}
}).done(function(data, textStatus, jqXHR) {
if(data == "invalid_credentials") {
$("#alert-wrong-credentials").show(100);
} else if(data == "ok") {
$("#alert-wrong-credentials").hide();
}
}).fail(function(jqXHR, textStatus, errorThrown) {
console.log(errorThrown);
});
I can successfully return the error string "invalid_credentials" when trying non-existing credentials, and I can see the user data when I enter the right ones:
user found: [{"_id":"58af514cb63980d2e8a51fed","user":"gus","pass":"123"}]
but I'm unable to redirect to the index.html page.
You should handle redirect on the client side once the login is complete. Redirect would work if you're actually visiting the LOGIN_ACTION_URL.
if(data == "invalid_credentials") {
$("#alert-wrong-credentials").show(100);
} else if(data == "ok") {
// Redirect
window.location.href = "index_url";
}
Also, there is a similar question here Node.js Page Redirect on AJAX login? With a function call after redirect? .
Ajax calls don't affect the current browser page. You send a request to a server. You get a response. If you want the current browser page to change, then you have to write Javascript that looks at the ajax response and then changes the current browser page by setting:
window.location = someURL;
So, doing a res.redirect() in response to an ajax call will just return a 302 status to the ajax call. That's it. If you want the ajax call to use that as an indicator to change the current browser page, then you have to write code to look for the 302 response and grab the right header from the response and then change window.location. There is no automated way to do that from an Ajax call.
When the browser is requesting a web page for a URL and it gets a 302 when loading a web page, then it will follow the redirect at that point. But, not for Ajax calls. An ajax call just gets you the response, whatever it is and it's up to your code that processes the response to actually do something with it.

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)

Posting to Twitter through OAuthSimple.js

I've been stuck on this one for a while. I'm trying to use OAuthSimple.js to interact with Twitter in a Chrome extension I've written.
The signing process seems to work fine for requests to retrieve a user's statuses, but I can't seem to construct a request that will successfully authenticate when I try to retweet, reply, or mark a tweet as favorite.
I'm following the guides here. I have also tried numerous ways of structuring the request, and comparing the request contents against the output of the OAuth tool provided by Twitter ( which seems to check out ), but I'm still getting 401 errors and generic "We couldn't authenticate you" responses.
Here's how I'm trying to form the request:
var sendTwitterRequest = function(url, params, method, callback) {
var request = null;
if ( localStorage.twitterAuthToken ) {
OAuthSimple().reset();
request = OAuthSimple(TwitterConsumerKey,TwitterConsumerSecret).sign({
action:method,
method:"HMAC-SHA1",
dataType:"JSON",
path:url,
parameters:params,
signatures:{
oauth_version:'1.0',
oauth_token:localStorage.twitterAuthToken,
oauth_secret:localStorage.twitterAuthVerifier
}
});
console.log(request);
$j.ajax({
url:request.signed_url,
type:method,
data:request.parameters,
success:callback
});
}
};
Then, making calls into this method like this:
// this works, I get the data and can do stuff with it
sendTwitterRequest('http://api.twitter.com/1/statuses/user_timeline.json?user_id=',null,'GET',someMethod());
// this fails and throws a 401 error every time
sendTwitterRequest("https://api.twitter.com/1/statuses/retweet/"+tweetKey+".json",null,'POST',someOtherMethod());
Am I missing something? Thanks in advance!
It turns out the requests I am creating are fine, I just needed a final one to exchange request tokens for OAuth tokens. I thought this step was covered when the user was prompted for input, but turns out I was wrong.
I also ended up switching from OAuthSimple.js to just OAuth.js, on account of the fact that I could only get OAuth.js to process both the token requests and the timeline requests.
Some of this is pretty specific to what my application is doing, so you will probably need to modify it.
The new sendTwitterRequest method:
var sendTwitterRequest = function(options){
var accessor={
consumerSecret:TwitterConsumerSecret
};
var message={
action:options.url,
method:options.method||"GET",
parameters:[
["oauth_consumer_key",TwitterConsumerKey],
["oauth_signature_method","HMAC-SHA1"],
["oauth_version","1.0"]
]
};
if(options.token){
message.parameters.push(["oauth_token",options.token])
}
if(options.tokenSecret){
accessor.tokenSecret=options.tokenSecret
}
for(var a in options.parameters) {
message.parameters.push(options.parameters[a])
}
OAuth.setTimestampAndNonce(message);
OAuth.SignatureMethod.sign(message,accessor);
try {
$j.ajax({
url:message.action,
async:options.async||true,
type:message.method||'GET',
data:OAuth.getParameterMap(message.parameters),
dataType:options.format||'JSON',
success:function(data) {
if (options.success) {options.success(data);}
}
});
} catch ( e ) {
}
};
And the methods that depend on it:
// asks Twitter for an oauth request token. User authorizes and the request token is provided
requestTwitterToken = function() {
// this is semi-specific to what my extension is doing, your callback string may need
// to be slightly different.
var callbackString = window.top.location + "?t=" + Date.now();
var params = [
[ 'oauth_callback', callbackString ]
];
sendTwitterRequest({
url: "https://api.twitter.com/oauth/request_token",
method: 'POST',
parameters: params,
format: 'TEXT',
success: function(data) {
var returnedParams = getCallbackParams(data);
if ( returnedParams.oauth_token ) {
chrome.tabs.create({
url:"https://api.twitter.com/oauth/authorize?oauth_token=" + returnedParams.oauth_token
});
}
},error:function( e ) {
console.log( 'error' );
console.log( e );
}
});
};
// exchanges the Twitter request token for an actual access token.
signIntoTwitter = function(token, secret, callback) {
var auth_url = "https://api.twitter.com/oauth/access_token";
var authCallback = function(data) {
var tokens = getCallbackParams(data);
localStorage.twitterAuthToken = tokens.oauth_token || null;
localStorage.twitterAuthTokenSecret = tokens.oauth_token_secret || null;
callback();
};
try {
sendTwitterRequest({url:auth_url, method:'POST', async:true, format:'TEXT', token:token, tokenSecret:secret, success:authCallback});
} catch ( e ) {
console.log(e);
}
};
With this, the steps are as follows:
ask Twitter for a token ( requestTwitterToken() ) and provide a callback
in the callback, check to see if a token is provided. If so, it's an initial token
pass the token back to Twitter and open the Twitter auth page, which allows the user to grant access
in the callback to this call, see if an access token was provided
exchange the request token for an access token ( signIntoTwitter() )
After that, I simply use the sendTwitterRequest() method to hit Twitter's API to fetch the timeline and post Tweets.

Categories