Twitter app OAuth with node.js - node.js

The following "should" work to post tweet with the contents of message below, i.e. "Lorem ipsum dolor..."
var OAuth = require("oauth").OAuth;
const twitterer = new OAuth( "https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
getContext().configuration.tasks.auto_tweet.apiAccessKey,
getContext().configuration.tasks.auto_tweet.apiAccessSecret,
// getContext().configuration.tasks.auto_tweet.apiPostKey,
// getContext().configuration.tasks.auto_tweet.apiPostSecret,
"1.0", null,"HMAC-SHA1");
// ... business logic
const message = "Lorem ipsum dolor..."
twitterer.post(
"https://api.twitter.com/1.1/statuses/update.json",
getContext().configuration.tasks.auto_tweet.apiPostKey,
getContext().configuration.tasks.auto_tweet.apiPostSecret,
({'status': message}),
"application/json",
function (error, data, response2) {
if(error){
failedTweets ++;
console.log("~~~~~~~~ Failed to send" , failedTweets, "tweets" );
console.log(error);
}
else{ sentTweets ++; }
console.log("~~~~~~~~ Sent" , sentTweets, "tweets" );
setTimeout(function(){
sendNextTweet();
},1000);
the result of the above, with the keys populated as in the twitter app dashboard is this error:
`{ statusCode: 401, data: '{"errors":[{"code":89,"message":"Invalid or expired token."}]}' }`
I'm at a loss here--this is within an app that needs to post to twitter as its own dedicated user, and it is not authenticating other users or anything of that sort, so I believe the callback_url is irrelevant... and in any case I don't know what the callback_url would be if it is required.
I have tried swapping the keys used s.t. the constructor uses the apiPostKey/secret while the post call uses the apiAccessKey/secret
I have generated new keys and updated the config with them.
I have verified that the server time is correct
Also, the 'Test OAuth' button on the app settings page (https://apps.twitter.com/app/XXXXX/settings) yields a page with this message "Sorry, that page doesn’t exist!" on it at https://dev.twitter.com/apps/XXXXX/oauth. It's not clear what this tells me, however

Turns out, the first bullet point was a lie. Most likely I "tested" when the app hadn't reloaded the swapped config. The issue was that the accessKey should be used in the post, and the "post" key (actually the consumer key) is the relevant value in the OAuth constructor.

From : https://dev.twitter.com/oauth/application-only
With Application-only authentication you don’t have the context of an
authenticated user and this means that any request to API for
endpoints that require user context, such as posting tweets, will not
work. However, the set of endpoints that will still be available can
have a higher rate limit.
And it won’t be able to:
Post tweets or other resources;
I recommend you to try use an node package for twitter :
for example
https://www.npmjs.com/package/twitter

Related

How to include access-token in the HTTP header when requesting a new page from browser

The similar question was asked by someone else (here) but got no proper answer. Since this is basic and important for me (and maybe for someone else as well), I'm trying to ask here. I'm using Node.js+Express+EJS on the server side. I struggled to make the token authentication succeeded by using jsonwebtoken at the server and jQuery's ajax-jsonp at the web browser. Now after the token is granted and stored in the sessionStorage at the browser side, I can initiate another ajax request with the token included in the request header, to get the user's profile and display it somewhere in the 'current' page. But what I want is to display a new web page to show the user's profile instead of showing it in the 'current' page (the main/index page of the website). The question is:
How to initiate such an HTTP GET request, including the token in the HTTP header; and display the response as a new web page?
How the Node.js handle this? if I use res.render then where to put the js logic to verify the token and access the DB and generate the page contents?
Or, should we say the token mechanism is more suitable for API authentication than for normal web page authentication (where the web browser provides limited API)?
I think the answer to this question is important if we want to use the token mechanism as a general authentication since in the website scenario the contents are mostly organized as web pages at the server and the APIs at the client are provided by the browser.
By pure guess, there might be an alternative way, which the ajax success callback to create a new page from the current page with the response from the server, but I have no idea of how to realize that as well.
By calling bellow code successfully returned the HTML contents in customer_profile.ejs, but the client side ajax (obviously) rejected it.
exports.customer_profile = function (req, res) {
var token = req.headers.token;
var public_key = fs.readFileSync(path.resolve() + '/cert/public_key.pem');
var decoded = jwt.verify(token, public_key);
var sql = 'SELECT * FROM customer WHERE username = "' + decoded.sub + '"';
util.conn.query(sql, function (err, rows) {
if (!err) {
for (var i = 0; i < rows.length; i++) {
res.render('customer_profile', {customer_profile: rows[i]});
break;
}
}
});
};
I am trying to find a solution to this as well. Please note, I am using Firebase for some functionality, but I will try to document the logic as best as I can.
So far what I was able to figure out is the following:
Attach a custom header to the HTTP request client-side
// landing.js - main page script snippet
function loadPage(path) {
// Get current user's ID Token
firebase.auth().currentUser.getIdToken()
.then(token => {
// Make a fetch request to 'path'
return fetch(`${window.location.origin}/${document.documentElement.lang}/${path}`, {
method: 'GET',
headers: {'X-Firebase-ID-Token': token} // Adds unverified token to a custom header
});
})
.then(response => {
// As noted below, this part I haven't solved yet.
// TODO: Open response as new webpage instead of displaying as data in existing one
return response.text();
})
.then(text => {
console.log(text);
})
.catch(error => {
console.log(error);
});
}
Verify the token according to your logic by retrieving the corresponding header value server-side
// app.js - main Express application server-side file
// First of all, I set up middleware on my application (and all other setup).
// getLocale - language negotiation.
// getContext - auth token verification if it is available and appends it to Request object for convenience
app.use('/:lang([a-z]{2})?', middleware.getLocale, middleware.getContext, routes);
// Receives all requests on optional 2 character route, runs middleware then passes to router "routes"
// middleware/index.js - list of all custom middleware functions (only getContext shown for clarity)
getContext: function(req, res, next) {
const idToken = req.header('X-Firebase-ID-Token'); // Retrieves token from header
if(!idToken) {
return next(); // Passes to next middleware if no token, terminates further execution
}
admin.auth().verifyIdToken(idToken, true) // If token provided, verify authenticity (Firebase is kind enough to do it for you)
.then(token => {
req.decoded_token = token; // Append token to Request object for convenience in further middleware
return next(); // Pass on further
})
.catch(error => {
console.log('Request not authorized', 401, error)
return next(); // Log error to server console, pass to next middleware (not interested in failing the request here as app can still work without token)
});
}
Render and send back the data
// routes/index.js - main router for my application mounted on top of /:lang([a-z]{2})? - therefore routes are now relative to it
// here is the logic for displaying or not displaying the page to the user
router.get('/console', middleware.getTranslation('console'), (req, res) => {
if(req.decoded_token) { // if token was verified successfully and is appended to req
res.render('console', responseObject); // render the console.ejs with responseObject as the data source (assume for now that it contains desired DB data)
} else {
res.status(401).send('Not authorized'); // else send 401 to user
}
});
As you can see I was able to modularize the code and make it neat and clear bu use of custom middleware. It is right now a working API returning data from the server with the use of authentication and restricted access
What I have not solved yet:
As mentioned above, the solution uses fetch API and result of the request is data from server (html) and not a new page (i.e when following an anchor link). Meaning the only way with this code now is to use DOM manipulation and setting response as innerHTML to the page. MDN suggests that you can set 'Location' header which would display a new URL in the browser (the one you desire to indicate). This means that you practically achieved what both, you and I wanted, but I still can't wrap my head around how to show it the same way browser does when you follow a link if you know what I mean.
Anyways, please let me know what you think of this and whether or not you were able to solve it from the part that I haven't yet

Facebook API Nodejs

I'm trying post a message as admin of a facebook page, but my code keeps posting as my personal account to the "wall" of the page, not as the page itself.
When authorizing my application I ask for the right permissionscopes, and accept them. Although, when I check my permissions with /{USER-ID}/permissions, it says that I've denied the application to manage my pages. Any ideas why?
Here's my code for posting:
var parameters = {};
parameters.message = "Hello world, this is dog.";
parameters.access_token = req.session.access_token;
FB.api('{PAGE-ID}/feed', 'post', parameters, function (result) {
if (!result) {
return res.send(500, 'error');
} else if (result.error) {
if (result.error.type == 'OAuthException') {
result.redirectUri = FB.getLoginUrl({ scope: 'publish_actions,manage_pages,publish_pages', state: encodeURIComponent(JSON.stringify(parameters)) });
}
console.log(result);
return res.send(500, result);
}
res.send(result);
});
And yes, of course I use my page id instead of the placeholder in my code above.
*Update: It seems like the permissions aren't even asked for when authorizing the application. I added the email scope to the list and it appears when I re-authorize the application.
Could this have anything to do with my application not beeing approved?
"Submit for Login Review
Some of the permissions below have not been approved for use by Facebook.
Submit for review now or read more."

Stripe module issues at Parse

I am developing a project which integrates Stripe + Parse for iOS. It uses web hooks and Cloud code via node js. Currently i am in need of implementing a couple of functions:
cancel user subscription with flag atPeriodEnd;
subscribe cancelled customer once again (named multiple subscriptions via Stripe docs).
As for the first one: I'm sending a request as follows in Parse's API -
Stripe.Customers.cancelSubscription(request.params.customerID, 1, null)
but the second parameter, i.e. atPeriodEnd remains 0 when i receive Stripe's response and my webhook catches request for cancelling user immediately. Also i have checked Stripe's dashboard to see parameters that i pass and it says 'No query parameters'. Hope you can help me with this one out.
Second one: as i mentioned earlier user needs to have ability to subscribe once again after cancellation. That means that i already have a valid customer saved at Stripe and all i need is to 'attach' to him a new subscription. There is a method for this at Stripe docs:
stripe.customers.createSubscription("cus_00000000000", { plan: "planName" }, function(err, subscription) {
});
But i can't find similar to this in Parse's API. Hope you can help with this one out.
Sorry if there are some mistakes or misunderstandings for you - feel free to ask, i will answer as much clear as i can. Thanks!
Here is a workaround to #1 - make an http call directly to the stripe endpoint using Parse.Cloud.httpRequest. (I agree that Stripe.Customers.cancelSubscription in the Parse cloud module does not seem to be working)
Parse.Cloud.define("cancel", function(request, response) {
var user = request.user;
var customerStripeId = user.get("stripeId");
var key = "<stripe_api_key>"
var url = "https://api.stripe.com/v1/customers/" + customerStripeId + "/subscription"
Parse.Cloud.httpRequest({
method: 'DELETE',
params: { at_period_end: true, key: key },
url: url,
success: function() {
response.success()
},
error: function(httpResponse) {
console.error('Delete failed with response code ' + httpResponse.status);
response.failure()
}
});
});

Performing simple authentication in Angular and Node

I have been struggling with performing simple authentication in my angular node application. I am well aware that there are ready to use angular-express yeoman kits out there. But I want to understand this fuly and hence the attempt.
What I have been able to do so far is create a login form which connects to node server. It sens login name and password and receives a response back.
Where I am stuck is how to convert this simple interaction into a authentication process.
My Directory structure is as below
--ParentDirectory/
-client/
--css/
--lib/ /*all angular and jquery library files*/
--src/ /* All other angular modules, directives etc */
--app.js
--index.html /* default page associated with app.js
--login.js /*module login is independent of app.js module */
--login.html
-server/
--server.js /*restify code sits here */
app.js is where the main app resides.
So far it looks like :
angular.module('app',['']);
angular.module('app').controller('mainCtrl',function($scope){
$scope.hello = "Hello World";
});
Now First things first.. when a user visits my website i.e index.html page.. they will end up in this app and I would like for them to be re-directed to login.html page if they are not authenticated.
Any clues on how to achieve that ?
Moving on..
Login.html simply asks for a username and password (not showing the code here to keep things compact)
Login.js looks like this:
angular.module('loginApp',['common.webservice'])
.controller('loginCtrl',['$scope','WSLogin','$location','$window','Authen',function($scope,WSLogin,$location,$window,Authen){
$scope.message;
$scope.submit = function(){
var temp = {logonID: $scope.username,password: $scope.password};
WSLogin.save(temp,function(result){
Authen.isLogged = true;
$window.sessionStorage.token = result.token;
$scope.message = result.token;
$location.path("/main");
},function(err){
$scope.message = "Authentication failed. Pls retry";
});
};
$scope.logout = function(){
if (AuthenticationService.isLogged){
Authen.isLogged = false;
delete $window.sessionStorage.token;
$location.path("/");
}
}
}])
.factory('Authen', function() {
var auth = { isLogged :false };
return auth;
});
WSLogin is a resource that connects to the node serve on the path user/authenticate. This webservice is working fine so far.
[not showing the code here to keep things compact]
The server.js file looks like this:
var restify = require('restify');
var server = restify.createServer({
log: log,
name: 'demo'
});
server.listen(12345, function(){
console.log('%s listening at %s', server.name,server.url);
});
server.post('/user/authenticate',function(req,res,next){
if (!(req.params.logonID === "test" && req.params.password === "test")) {
res.send(401, 'Wrong user or password');
return;
}
var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john#doe.com',
id: 123
};
// we are sending the profile inside the token
res.json({token:profile);
});
How can I patch things up so that this somewhat resembles a authentication system.
I have picked up tips from various blogs, sites etc.. but no luck implementing them.
I understand you want to know how things are built, but I highly recommend picking up Passport to use on the Node/server side for authentication. With that said, in your implementation of login, it might be easier to include a cookie with the token in the response, rather than returning the token in the response. The code can then return a 200 (or 201) response code to indicate the login was successful, but by moving the token to a cookie, the client code doesn't have to deal with sending the token on future requests -- it's sent automatically in a cookie.
Moving on to your client side questions, keep in mind that you would enable security to protect resources on the server side. Anything you put in your client side JavaScript code can be read by anyone. So in the end these APIs that return protected data are the ones that need to be protected. Again, Passport provides an easy way to protect an API, but you could validate that each API request contains this token (or cookie) prior to providing the data.
In the case that the request is not authorized (it doesn't contain the token, or the token is invalid), you can return a 401 (Unauthorized) response. In the client side code, you can check for this and automatically route the user to the Login page.
Using Angular, one pattern to accomplish this is to use httpProvider Interceptors which allow you to hook into each HTTP request. You could check for a responseError with a status of 401, and route them to the Login page.

How do you do "remember me" functionality in AngularJS?

I have a simple app I'm building using Play + AngularJS that requires authentication before most routes can be accessed. The login flow includes a "remember me" feature that stores a session ID in to the browser local storage and gets mapped to a valid authorized database session entry on the server side any time a user returns to the app.
The problem I'm having is that I do the session checking (extract cookie & compare against server) in the run() function of the module:
.run(function ($rootScope, $http, $cookieStore, $location) {
// <snip>
// check if there is already a session?
var sessionId = window.localStorage["session.id"];
if (sessionId == null) {
sessionId = $cookieStore.get("session.id");
}
if (sessionId != null) {
$http.get("/sessions/" + sessionId)
.success(function (data) {
$http.defaults.headers.common['X-Session-ID'] = data.id;
$cookieStore.put("session.id", data.id);
$rootScope.user = data.user;
})
.error(function () {
// remove the cookie, since it's dead
$cookieStore.remove("session.id");
window.localStorage.removeItem("session.id");
$location.path("/login");
});
} else {
if ($location.path() != "/login" && $location.path() != "/signup") {
$location.path("/login");
}
}
});
The problem is that this function executes an AJAX call and I don't know if the session is valid until it completes. However, the controller that loads (via the route selected by $routeProvider) can fire away another AJAX call that often kicks off before the other one finishes, resulting in a race condition and the initial request getting a 401 response code.
So my question is: how can I force run (with its associated $http call) to complete before any other part of the app runs? I have tried using $q/promise here and it doesn't seem to make a difference (perhaps run functions don't honor promises). I've been advisor to use resolve feature in $routeProvider but I don't know exactly what to do and I'm not super execited about having to put that in for every route anyway.
I assume this is a pretty common use case and it gets solved every day. Hopefully someone can give me some direction with my code, or share their approaches for "remember me" and AngularJS.
You need to manual bootstrap your app after you get session from server.It's easy if you use jQuery for example you can do, or even without jQuery you can use injector to access $http before bootstrapping
$.get(server,function(){
//success , set variable.
}).fail(function (){
//failed :( redirect to login or set session to false etc... null
})
.always(function(){
//alwyas bootstrap in both case and set result as a constant or variable Angular.module('app').variable('session',sessionResult);
});
I'm on phone right now, but this should give u the idea

Resources