CORS + sendAjax + authentication - ajax

I am using casperjs and have run more 1000 test runs. I was using sendAjax function to fetch json objects from couchdb and it was working without any issue.
Yes I do have --web-security in my grunt file and using phantomjs with linux mint 13 64 bit.
I can access the data manually by log in to couchdb interface and use the url to fetch the records from the database. I am unable to fetch the data from couchdb using the script below.
var url = 'http://worksite.com:5984/draft/_design/WidgetData/_view/by_rootIdforTest?include_docs=true&startkey="52210def794e1e7a8dd8e74b8b0ab5a6"&endkey="52210def794e1e7a8dd8e74b8b0ab5a6"'
var jsonObject = casper.evaluate(function(url) {
try {
return JSON.parse(__utils__.sendAJAX(url, 'GET', null, false));
} catch (e) {}
}, {
url: url
});
return jsonObject;
Modified script and added authentication credentials but request is not being authorized
var url = 'http://worksite.com:5984/draft/_design/WidgetData/_view/by_rootIdforTest?include_docs=true&startkey="52210def794e1e7a8dd8e74b8b0ab5a6"&endkey="52210def794e1e7a8dd8e74b8b0ab5a6"'
var jsonObject = casper.evaluate(function(url) {
try {
return JSON.parse(__utils__.sendAJAX(url, 'GET', {
"name": "username",
"password",
"mypassword"
}, false));
} catch (e) {}
}, {
url: url
});
return jsonObject;

Related

Axios - Request header content-type was not present in the Access-Control-Allow-Headers list - ElasticSearch

I'm new to a lot of this technology, but I think I've diagnosed my issue and need some help. I've seen numerous posts on SO regarding this issue, but none have worked, though they have helped me diagnose issue.
I believe the issue is when I send the Header Content-Type w/ my pre-flight w/ Axios, it fails. This is possibly due to lower/case upper case? The error has lower case, but I tried both on the server without luck.
Basically, if I don't specify any header and Axios uses json as content-type, it works, but as soon as I specify Content-Type my pre-flight fails (even though I think post would work..).
Here is the elasticsearch.yml
cluster.name: "docker-cluster"
network.host: 0.0.0.0
http.cors.enabled : true
http.cors.allow-origin: "*"
http.cors.allow-methods: OPTIONS,HEAD,GET,POST,PUT,DELETE
http.cors.allow-headers: X-Requested-With,X-Auth-Token,Content-Type,Content-Length
#http.cors.allow-credentials: true
Here is my JS that I'm testing BTW w/ an Office Add-In solution in Visual Studio 2017 which I think is using IE as a browser.
Main Func:
var URL = "https://elasticsearch:9200/users/_search"
const data = {
"query": {
"match": {
"name": "freesoftwareservers"
}
}
};
Do_Axios('get', URL, data, null, false)
Do_Axios('post', URL, data, null, false)
Do_Axios:
async function Do_Axios(method, URL, data, headers, withCredentials) {
return axios({
method: method,
url: URL,
withCredentials: withCredentials,
//contentType: 'application/json', // does nothing
//data: JSON.stringify(data), //Causes urlformencoded which is wrong
data: data, //caues type to be json and I get error
headers: {
//"Content-Type": "application/json"
},
})
.then(function (response) {
console.log("Axios " + method + " response:");
console.log(response)
return response;
})
.catch(function (error) {
console.log(error);
});
}
Note: I can get/post if I comment out //data but then the post doesn't run my query. If I uncomment data then Axios uses urlformencoded but that doesn't work.
For now, I've been able to search API via urlformencoded queries, but I'd like to fix my ability to POST correctly to resolve future errors. I'm unsure if issue should be pointed to Axios or Elasticsearch if I open a request.
Well, I finally figured it out. I wonder how many of the other posts I read have similar issues... anyway, the issue was w/ my NGinX proxy server. No better way to learn about CORS then to setup an API and make CORS requests via IE! Without the below, I was still able to post w/ POSTMAN to the same URL which hit my nginx server, but the call from Axios/IE/JS Evironment failed.
I found these snippets and this was the magic that needed added to my "regular" configuration:
proxy_pass_header Access-Control-Allow-Origin;
proxy_pass_header Access-Control-Allow-Methods;
proxy_hide_header Access-Control-Allow-Headers;
add_header Access-Control-Allow-Headers 'X-Requested-With, Content-Type';
add_header Access-Control-Allow-Credentials true;
https://gist.github.com/sahilsk/b16cb51387847e6c3329
Here is my code as it stands, cleaned up but generic atm:
Note: I pass axios because I can't figure out how to get my Webpack to transform/polyfill my funcs in seperate js files. But I can declare axios in the main func and pass it and then I can move my funcs into separate files as needed for organization. There is likely a better way to do without passing axios and configuring webpack
Main Func:
var username = "freesoftwareservers"
var ipv4 = "192.168.1.255"
var showhelp = "false"
await Do_AddUserToES(axios,username, ipv4, showhelp)
Get_UserFromES(axios,username)
var index = "users"
var query = {
query: {
match: {
"username": username
}
}
};
Get_PostQueryToES(axios,query, index)
Funcs:
function Do_Axios(axios, method, URL, data, headers, withCredentials) {
return axios({
method: method,
url: URL,
withCredentials: withCredentials,
data: data,
headers: headers,
})
.then(function (response) {
console.log("Axios " + method + " response:");
console.log(response)
return response;
})
.catch(function (error) {
console.log(error);
});
}
function Get_ESURL(Bool_Search, Bool_Doc, Bool_Update, Opt_Index, Opt_IndexKey) {
var ESUrl = "https://elasticsearch:9200"
var ESSearch = "/_search"
var ESDoc = "/_doc"
var ESUpdate = "/_update"
var ReturnURL = ESUrl
if (Opt_Index != undefined) { ReturnURL = ReturnURL + "/" + Opt_Index }
if (Bool_Search == true) { ReturnURL = ReturnURL + ESSearch }
if (Bool_Doc == true) { ReturnURL = ReturnURL + ESDoc }
if (Bool_Update == true) { ReturnURL = ReturnURL + ESUpdate }
if (Opt_IndexKey != undefined) { ReturnURL = ReturnURL + "/" + Opt_IndexKey }
console.log("ReturnURL:" + ReturnURL)
return ReturnURL;
}
function Do_AddUserToES(axios, username, ipv4, showhelp) {
var adduser = {
"username": username,
"ipv4": ipv4,
"showhelp": showhelp
};
var URL = Get_ESURL(false, true, false, "users", username)
return Do_Axios(axios, 'post', URL, adduser, null, false);
}
function Get_UserFromES(axios, username) {
var URL = Get_ESURL(false, true, false, "users", username)
return Do_Axios(axios, 'get', URL, null, null, false);
}
function Get_PostQueryToES(axios, query, index) {
var URL = Get_ESURL(true, false, false, index)
return Do_Axios(axios, 'post', URL, query, null, false);
}

Parse On Buddy Logout User

I am migrating an application from parse.com to buddy.com. One of the caveats of the migration was that Parse.User.current() is no longer available on buddy.com, instead you have to get the user and session token from the request itself: https://github.com/ParsePlatform/Parse-Server/wiki/Compatibility-with-Hosted-Parse#no-current-user
The application I am migrating has a logoutUser method that I am attempting to migrate:
Parse.Cloud.define("logoutUser", function(request, response) {
Parse.User.logOut().then(
function onSuccess(result){
response.success(result);
},
function onError(error) {
response.error(error);
}
)
});
now I am attempting to do this in the new style, but am receiving an error. (NOTE: This is cloud code not a nodejs environment)
{
"code":"500",
"error":"Error: There is no current user user on a node.js server environment."
}
New implementation:
function logoutUser(request, response) {
var user = request.user;
var sessionToken = user.getSessionToken();
Parse.User.logOut({ sessionToken }).then(
function onSuccess(result){
response.success(result);
},
function onError(error) {
response.error(error);
}
)
}
Parse.Cloud.define("logoutUser", function(request, response) {
logoutUser(request, response);
});
Suggestions on how to correctly log out users in the Parse on Buddy cloud code?
You could fetch user's session or sessions and delete it / them:
var query = new Parse.Query("_Session");
query.descending('createdAt');
query.equalTo('user', {__type:"Pointer", className:"_User", objectId:"idhere"});
query.first({
useMasterKey: true
}).then(function(session) {
var sessions = [];
sessions.push(session);
Parse.Object.destroyAll(sessions);
}, function (err) {
console.log("Internal error " + err);
});
OR for more tokens you could use find instead of first like:
var query = new Parse.Query("_Session");
query.equalTo('user', {__type:"Pointer", className:"_User", objectId:"idhere"});
query.find({
useMasterKey: true
}).then(function(sessions) {
Parse.Object.destroyAll(sessions);
}, function (err) {
console.log("Internal error " + err);
});
The above will mostly delete or tokens related to the given user. If you wish to delete only tokens used for login, and not for signup or upgrade, then you could put into your query:
query.equalTo('createdWith', { action: 'login', authProvider: 'password'});
As far as i know, deleting a user's last used for login token, then he is logged-out.
To add to the above, if you pass up the user's session key to the Cloud Code function via the X-Parse-Session-Token header, you can use the populated request.user object in the session query directly, instead of the user's ID.

Parse server twitter authentication: Twitter auth integrated but unable to create session to use on client side

Parse Cloud code:
Parse.Cloud.define("twitter", function(req, res) {
/*
|--------------------------------------------------------------------------
| Login with Twitter
| Note: Make sure "Request email addresses from users" is enabled
| under Permissions tab in your Twitter app. (https://apps.twitter.com)
|--------------------------------------------------------------------------
*/
var requestTokenUrl = 'htt****/oauth/request_token';
var accessTokenUrl = 'http***itter.com/oauth/access_token';
var profileUrl = 'https://api.twitter.com/1.1/account/verify_credentials.json';
// Part 1 of 2: Initial request from Satellizer.
if (!req.params.oauth_token || !req.params.oauth_verifier) {
var requestTokenOauth = {
consumer_key: 'EVJCRJfgcKSyNUQgOhr02aPC2',
consumer_secret: 'UsunEtBnEaQRMiq5yi4ijnjijnjijnijnjEjkjYzHNaaaSbQCe',
oauth_callback: req.params.redirectUri
};
// Step 1. Obtain request token for the authorization popup.
request.post({
url: requestTokenUrl,
oauth: requestTokenOauth
}, function(err, response, body) {
var oauthToken = qs.parse(body);
// console.log(body);
// Step 2. Send OAuth token back to open the authorization screen.
console.log(oauthToken);
res.success(oauthToken);
});
} else {
// Part 2 of 2: Second request after Authorize app is clicked.
var accessTokenOauth = {
consumer_key: 'EVJCRJfgcKSyNUQgOhr02aPC2',
consumer_secret: 'UsunEtBnEaQRMiq5yi4ijnjijnjijnijnjEjkjYzHNaaaSbQCe',
token: req.params.oauth_token,
verifier: req.params.oauth_verifier
};
// Step 3. Exchange oauth token and oauth verifier for access token.
request.post({
url: accessTokenUrl,
oauth: accessTokenOauth
}, function(err, response, accessToken) {
accessToken = qs.parse(accessToken);
var profileOauth = {
consumer_key: 'EVJCRJfgcKSyNUQgOhr02aPC2',
consumer_secret: 'UsunEtBnEaQRMiq5yi4ijnjijnjijnijnjEjkjYzHNaaaSbQCe',
token: accessToken.oauth_token,
token_secret: accessToken.oauth_token_secret,
};
console.log(profileOauth);
// Step 4. Retrieve user's profile information and email address.
request.get({
url: profileUrl,
qs: {
include_email: true
},
oauth: profileOauth,
json: true
}, function(err, response, profile, USER) {
console.log(profile);
//console.log(response.email);
Parse.Cloud.useMasterKey();
var UserPrivateInfo = Parse.Object.extend("UserPrivateInfo");
var query = new Parse.Query(UserPrivateInfo);
query.equalTo("email", profile.email);
query.first({
success: function(privateInfo) {
if (privateInfo) {
res.success(privateInfo.get('user'));
} else {
response.success();
}
},
error: function(error) {
response.error("Error : " + error.code + " : " + error.message);
}
});
});
});
}
});
For client side using Sendgrid twitter authentication:
loginCtrl.twitterLogin = function() {
$auth.authenticate("twitter").then(function(response) {
console.log(response.data.result);
var user = response.data.result;
if (!user.existed()) {
var promise = authFactory.saveUserStreamDetails(user, response.email);
promise.then(function(response) {
signInSuccess(response);
}, function(error) {
console.log("error while saving user details.");
});
} else {
signInSuccess(user);
}
}).catch(function(error) {
console.log(error);
});;
};
Issue:
Step 1: Called cloud function Parse.Cloud.define("twitter", function(req, res) using loginCtrl.twitterLogin
Step 2: Twitter popup opens and user logs in to twitter
Step 3: Got verification keys and again cloud function Parse.Cloud.define("twitter", function(req, res) is called and user is verified
Step 4: Got the user email using the twitter API.
Step 5: I can get the existing Parse User Object using the email or can signUp using that email.
Step 6: Returns the parse user object to client but there is no session attached to it so **How can I create user session?
Without parse session we can not log in to parse application. Every clound code api/ function call will fail as no session is attached to them. So how can I create and manage a session using twitter authentication.

getSessionToken() not working in Parse

Below is my parse cloud code. I am unable to get user session even when I am using master key. Is it not possible to get User session even if we use mater key to retrieve user?
Parse.Cloud.define("getFSUserSession", getFSUserSession);
function getFSUserSession(request, response) {
Parse.Cloud.useMasterKey();
//Removed some codes here to make it short
var someID = request.params.user;
var query = new Parse.Query(Parse.User);
query.equalTo('familySearchID', someID);
query.ascending('createdAt');
query.first({useMasterKey: true}).then(function (user) {
console.log(typeof user.getSessionToken());//this returns function
console.log(user.getSessionToken()); //this logs No Message provided
response.success(user.getSessionToken());
})
}
I had to disable Require Revocable Sessions to make this work. After disabling Require Revocable Sessions in settings we can make an api request to /1/upgradeToRevocableSession to get the upgraded session token.
Parse.Cloud.define("getFSUserSession", getFSUserSession);
function getFSUserSession(request, response) {
Parse.Cloud.useMasterKey();
//Removed some codes here to make it short
var someID = request.params.user;
var query = new Parse.Query(Parse.User);
query.equalTo('familySearchID', someID);
query.ascending('createdAt');
query.first({useMasterKey: true}).then(function (user) {
return Parse.Cloud.httpRequest({
method: 'POST',
url: 'https://api.parse.com/1/upgradeToRevocableSession',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-REST-API-Key': restKey,
'X-Parse-Session-Token': user.getSessionToken()
}
});
}).then(function (httpResponse) {
var sessionObject = httpResponse.data;
if (sessionObject.sessionToken) {
return Parse.Promise.as(sessionObject.sessionToken);
}
return Parse.Promise.error('Unable to get Session token');
});
}

Ember Simple Auth on Firefox: authentication throws Error

I am extending Ember Simple Auth's base authentication class to allow authentication with Google. So far, it works on Safari 8 and Chrome 41 (both on Yosemite) with no errors. However, on Firefox 35, it throws an Error that does not occur on the other browsers. Here is my Google authenticator class:
App.GoogleAuthenticator = SimpleAuth.Authenticators.Base.extend({
// constants for Google API
GAPI_CLIENT_ID: 'the client id',
GAPI_SCOPE: ['email'],
GAPI_TOKEN_VERIFICATION_ENDPOINT: 'https://www.googleapis.com/oauth2/v2/tokeninfo',
// method for scheduleing a single token refresh
// time in milliseconds
scheduleSingleTokenRefresh: function(time) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.run.later(self, function() {
gapi.auth.authorize({
client_id: self.GAPI_CLIENT_ID,
scope: self.GAPI_SCOPE,
immediate: true
}, function(data) {
if (data && !data.error) {
resolve(data);
} else {
reject((data || {}).error);
}
});
}, time);
});
},
// WIP: recursive method that reschedules another token refresh after the previous scheduled one was fulfilled
// usage: scheduleTokenRefreshes(time until token should refresh for the first time, time between subsequent refreshes)
// usage: scheduleTokenRefreshes(time between refreshes)
scheduleTokenRefreshes: function(time1, time2) {
var self = this;
// if there is a time2, schedule a single refresh, wait for it to be fulfilled, then call myself to schedule again
if (!Ember.isEmpty(time2)) {
self.scheduleSingleTokenRefresh(time1)
.then(function() {
self.scheduleTokenRefreshes(time2);
});
// if there isn't a time2, simply schedule a single refresh, then call myself to schedule again
} else {
self.scheduleSingleTokenRefresh(time1)
.then(function() {
self.scheduleTokenRefreshes(time1);
});
}
},
// method that restores the session on reload
restore: function(data) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
console.log(data);
if (Ember.isEmpty(data.access_token)) {
reject();
return;
}
// schedule a refresh 15 minutes before it expires or immediately if it expires in < 15
var timeNow = Math.floor(Date.now() / 1000);
var expiresAt = +data.expires_at;
var timeDifference = expiresAt - timeNow;
var schedulingDelay = Math.floor(timeDifference - 15 * 60);
schedulingDelay = schedulingDelay < 0 ? 0 : schedulingDelay;
self.scheduleTokenRefreshes(schedulingDelay * 1000, 45 * 60);
resolve(data);
});
},
// method that authenticates
authenticate: function() {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
gapi.auth.authorize({
client_id: self.GAPI_CLIENT_ID,
scope: self.GAPI_SCOPE
}, function(data) {
if (data && !data.error) {
// schedule a refresh in 45 minutes
var schedulingDelay = 45 * 60;
self.scheduleTokenRefreshes(schedulingDelay * 1000);
resolve(data);
} else {
reject((data || {}).error);
}
});
});
},
// method that logs the user out and revokes the token
invalidate: function(data) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
// send a GET request to revoke the token
Ember.$.ajax({
type: 'GET',
url: 'https://accounts.google.com/o/oauth2/revoke?token=' + self.get('session.access_token'),
contentType: 'application/json',
dataType: 'jsonp'
})
.done(function(successData) {
resolve(successData);
})
.fail(function(error) {
reject(error);
});
});
}
});
When the popup window closes after a successful login on Google's end, this error appears on Firefox's console:
Error: Assertion Failed: Error: Permission denied to access property 'toJSON' ember.js:13749
"__exports__.default<.persist#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1524:1
__exports__.default<.updateStore#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1195:11
__exports__.default<.setup#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1149:9
__exports__.default<.authenticate/</<#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1066:13
tryCatch#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:47982:16
invokeCallback#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:47994:17
publish#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:47965:11
#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:29462:9
Queue.prototype.invoke#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:848:11
Queue.prototype.flush#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:913:13
DeferredActionQueues.prototype.flush#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:718:13
Backburner.prototype.end#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:143:11
createAutorun/backburner._autorun<#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:546:9
" ember.js:29488
Here is the version information:
DEBUG: Ember : 1.9.1
DEBUG: Ember Data : 1.0.0-beta.14.1
DEBUG: Handlebars : 2.0.0
DEBUG: jQuery : 2.1.3
DEBUG: Ember Simple Auth : 0.7.2
The most confounding thing is that this only appears on Firefox. Is it a bug in Ember Simple Auth or Ember? How do I fix it?
I do not know about only Firefox throwing an error (I've had a similar error with Chrome 40), but there is a bug in ember-simple-auth 0.7.2 with Ember 1.9 that prohibits sending an actual error response in the authenticate method in the authenticator.
If you return reject() in the rejection function of authenticate it will not throw an additional error. This will however not propagate the errorstatus or message, so I consider this a bug.
A work-around was proposed on github about this issue by setting Ember.onerror=Ember.K temporarily so additional errors will not be propagated, although it will propagate the original authenticate rejection with the error-status.
The issue in the github repo only mentions problems with testing this, but I've had this problem in normal code.
see: https://github.com/simplabs/ember-simple-auth/issues/407
Turns out the error was on the resolve part of the authenticate method. Here is what fixed it:
App.GoogleAuthenticator = SimpleAuth.Authenticators.Base.extend({
authenticate: function() {
return new Ember.RSVP.Promise(function(resolve, reject) {
gapi.auth.authorize({
client_id: 'the client id',
scope: ['the scopes'],
}, function(data) {
if (data && !data.error) {
resolve({
access_token: data.access_token // !! passing the entire 'data' object caused the error somehow
});
} else {
reject((data || {}).error);
}
});
});
},
// ...
});
I'm still not quite sure why this caused the error. Perhaps the Google API's response (in its entirety) is somehow incompatible with Ember Simple Auth.

Resources