Node.js twitter auth redirect - node.js

I want to enable oauth login with twitter.
I added the following code for everyauth in server.js
everyauth
.twitter
.consumerKey('mykey')
.consumerSecret('mysecret')
.findOrCreateUser(function (sess, accessToken, accessSecret, twitUser) {
return usersByTwitId[twitUser.id] || (usersByTwitId[twitUser.id] = addUser('twitter', twitUser));
})
.redirectPath('/about');
where 'mykey' and 'mysecret' are the correspoding of my twitter app.
The settings in my twitter app are:
Access level: Read and write
About the application permission model
Consumer key: mykey
Consumer secret: mysecret
Request token URL: https://api.twitter.com/oauth/request_token
Authorize URL: https://api.twitter.com/oauth/authorize
Access token URL: https://api.twitter.com/oauth/access_token
Callback URL: http://192.168.1.197:8002/
Sign in with Twitter: Yes
The problem is after correct loging, the web stays in 'https://api.twitter.com/oauth/authenticate' and doesn't redirect to my callback url
What could be wrong?
UPDATE:
now I'm getting one of the following errors...
{"errors": [{"message": "The Twitter REST API v1 is no longer active.
Please migrate to API v1.1. https://dev.twitter.com/docs/api/1.1/overview.", "code": 68}]}
I really don't know how to update to version 1.1. I tried some solutions from the dev twitter forum but nothing.
500 Error: Step rememberTokenSecret of `twitter` is promising:
requestTokenSecret ; however, the step returns nothing.
Fix the step by returning the expected values OR by returning a Promise
that promises said values.
MORE INFO
I updated everyauth and twitter api. I check the file twitter.js in the everyauth's node_module folder
...
.fetchOAuthUser( function (accessToken, accessTokenSecret, params) {
var promise = this.Promise();
this.oauth.get(this.apiHost() + '/1.1/users/show.json?user_id=' + params.user_id, accessToken, accessTokenSecret, function (err, data, res) {
if (err) {
err.extra = {data: data, res: res};
return promise.fail(err);
}
var oauthUser = JSON.parse(data);
promise.fulfill(oauthUser);
});
return promise;
})
...
Thank you.

Related

Post To Twitter on Behalf of Another User using Node.js, express, twit, and passport-twitter

Using the Twitter API, I'm trying to post a tweet on behalf of a Twitter user.
I'm using node and the passport-twitter and twit modules.
Some resources:
I followed this video tutorial NodeJS and ExpressJS: Using Twitter
authentication via Passport.
Sourcecode for the tutorial
here.
Passport-twitter documentation
twit documentation
I successfully authenticated with passport-twitter following the tutorial above.
I also successfully posted using twit on my twitter developer account.
However, I'm having trouble combining these two things; trying to post to twitter on behalf of another user. For that, I need to get the user's access token and access token secret. Then, I need to make a post request to the Twitter API with that information.
I'm not sure where to put the post request in the passport-twitter code. I tried putting it in the second route which is the URL to which Twitter redirects the user after they have signed in.
app.get('/twitter/login', passport.authenticate('twitter'))
app.get('/twitter/return', passport.authenticate('twitter', {
failureRedirect: '/'
}), function(req, res) {
//Post using twit
//grab access token and access token secret from the request query
const access_token = req.query.oauth_token;
const access_token_secret = req.query.oauth_verifier;
//set the configurations with the access token and access token secret that we just got
const config = {
consumer_key: <consumer key here>,
consumer_secret: <consumer secret here>,
access_token,
access_token_secret,
timeout_ms: 60*1000,
strictSSL: true
}
//pass in the configurations
var T = new Twit(config);
//post
T.post('statuses/update', { status: 'hello world!' }, function(err, data, response) {
if (err)console.log("oops, didn't tweet: ", err.message);
})
res.redirect('/');
})
But I got an error: Invalid or expired token.
I expected it to work because the authentication worked.
This is my first time using OAuth so perhaps I misunderstand how this all works.
Where am I supposed to put the post request?
UPDATE:
I tried posting to my dev account, using my dev account's access token and secret. It worked. Which lead me to believe there is something wrong with the access token and secret for the user.
I think I know partially what’s going.
I assumed that the property oauth_verifier found in the request query object was the access token secret.
const access_token_secret = req.query.oauth_verifier;
But now I don’t think oauth_verifier is the same as the access token secret. oauth_verifier has less characters than my dev account’s access token secret. So it seems that the datatypes are different.
But now I’m trying to figure out where the access token secret is? There are only two properties in the request query object (req.query);
oauth_token
oauth_verifier
Where’s the access token secret for the user?
I solved my issue. It was in the documentation for passport-twitter all along. Man, I spent days on this issue.
The strategy also requires a verify callback, which receives the access token and corresponding secret as arguments, as well as profile which contains the authenticated user's Twitter profile.
-from passport-twitter readMe
In the example in the docs, you can see token and tokenSecret in the params.
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/twitter/callback"
},
function(token, tokenSecret, profile, cb) {
User.findOrCreate({ twitterId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
I read this and saw this before. But assumed this was the the consumer key and consumer secret. I didn't realize it was what I was looking for: the access token and access secret.
So your post to twit would go something like this:
passport.use(new Strategy({
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
callbackURL: 'http://localhost:3000/twitter/return'
}, function(token, tokenSecret, profile, callback) {
const configs = createConfigs(token, tokenSecret);
// Post to twitter
var Twit = require('twit')
var T = new Twit({
consumer_key: '...', //get this from developer.twitter.com where your app info is
consumer_secret: '...', //get this from developer.twitter.com where your app info is
access_token: token,
access_token_secret: tokenSecret,
timeout_ms: 60*1000, // optional HTTP request timeout to apply to all requests.
strictSSL: true, // optional - requires SSL certificates to be valid.
})
//
// tweet 'hello world!'
//
T.post('statuses/update', { status: 'hello world!' }, function(err,
data, response) {
console.log(data)
})
return callback(null, profile);
}));

Battle.net API returns 401 Error using OAuth Token

I'm using Nodejs with express on the Battle.net API to generate an Oauth Token. https://develop.battle.net/documentation/guides/using-oauth
Generating the token itself works, as it returns me the token. But when I use the code to make a request to their API, for example:
https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN
I get a 401 Unauthorized Error Response, debug log:
{ url:
'https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN',
status: 401,
statusText: 'Unauthorized',
headers: Headers { [Symbol(map)]: [Object] } }
I'm trying to fetch the members of a guild via fetch().
I already tried:
Creating a new Application (with new Client Secret and ID)
setting every possible callback url in the battle.net settings:
https://localhost/
http://localhost/
https://localhost:443/
http://localhost:443/
https://localhost/auth/bnet/callback
http://localhost/auth/bnet/callback
https://localhost:443/auth/bnet/callback
http://localhost:443/auth/bnet/callback
creating a token manually by "trying out the api" (https://develop.battle.net/documentation/api-reference/world-of-warcraft-community-api), where you put in your Client ID and Secret and then get a temporary Token. THAT ONE works, also in my application.
You can compare response of those two urls (just use your browser):
First (Generated in my application): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=EU7XD8E4K9IAJKBGJSP3MDBLAVCIU2BYXS
Second (Generated trying out the API on battle.net website where you fill out clientid and secret to test out the api): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=US23su4g0hAeS5w3EUCkKA9MJPgJ8k8bzV
CODE
server.js, simple express app
var BNET_ID = "MYID";
var BNET_SECRET = "MYSECRET";
...
// Use the BnetStrategy within Passport.
passport.use(
new BnetStrategy(
{ clientID: BNET_ID,
clientSecret: BNET_SECRET,
scope: "wow.profile sc2.profile",
callbackURL: "https://localhost/",
region: "eu" },
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
return done(null, profile);
});
})
);
// bnet auth routes
app.get('/auth/bnet', passport.authenticate('bnet'));
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/' }),
function(req, res){
res.redirect('/');
});
controller.js
...
const res = await fetch(`https://eu.api.blizzard.com/wow/guild/${servers[iterator]}/The new Dimension?fields=members&locale=de_DE&access_token=${thetoken}`).then((res) => {
res.json();
// for debugging, shows 401 Error
console.log(res);
});
...
I actually expect a response such as this, because it works using a temporary Token:
status: 200 OK
body: {
"lastModified": 1546676373000,
"name": "The new Dimension",
"realm": "Blackmoore",
"battlegroup": "Glutsturm / Emberstorm",
"level": 25,
"side": 0,
"achievementPoints": 1005,
"members":
(......)
}
I managed to resolve the issue!
Very, very hacky but I managed to resolve the issue by hacking the oauth callback middleware like this:
set my used API token to the req.user.token.
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/?error' }),
function(req, res) {
req.session.bnettoken = req.user.token;
res.redirect('/');
}
);
I suspect that "code" or "token" is also used in my SessionStorage (express-session) to store the current session in my database. So I just hack the user.token out of the request and use that. Phew.. Hours of work.
From the documentation I can see that you need to pass the token into the Authorization header with value of : Bearer HEREISMYTOKEN.
More info on Authorization Header and Headers here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Headers
Example on how to use it can be found in this SO answer

express passport-linkedin, making api requests

I'm using express and passport-linkedin. The authentication flow works fine, I can get the user's basic profile, and now I want to make an API request. /v1/people/~/connections, for example.
I've done some reading and trial and error, but all I get is Wrong Authentication Scheme or Internal Server Error.
I have the token and tokenSecret given to the verify callback of the LinkedInStrategy instance. How do I go from those to an authorized API request?
I use a 'isLegit' function running as middleware.
Example :
app.get('/api/mysecuredpage',isLegit,function(req,res){
res.render('mysecurepage');
});
function isLegit(req, res, next) {
// if user is authenticated in the session, next
if (req.isAuthenticated())
return next();
// if they aren't send an error
res.json({error:'You must be logged in to view information'});
}
EDIT :
To make a request to the linkedIn api, just set up your request with the following options :
var options = {
url: 'https://api.linkedin.com/v1/people/~/connections',
headers: { 'x-li-format': 'json' },
qs: { oauth2_access_token: user.access_token }
};
request(options,function(err,res,body){
console.log(body);
});
access_token is the name of the variable in your passport strategy, it could be different depending how you've set it up. basically, it's one of the fields of your authenticated user ;-)

Node.js OAuth2: Get Google+ activities

From today I'm not able to retrieve my Google+ activities by placing a GET request to this url:
https://www.googleapis.com/plus/v1/people/me/activities/public?key={API_KEY}
I'm getting a 401 error. So I tried to sign the request using the node.js oauth2 library, including a pre-generated user token, but I'm getting the same error. The js code for my test script is:
var OAuth2 = require('oauth').OAuth2,
GOOGLEPLUS_APP_ID = {APP_ID},
GOOGLEPLUS_APP_SECRET = {APP_SECRET},
accessToken = {USER_TOKEN};
var _oauth2 = new OAuth2(GOOGLEPLUS_APP_ID, GOOGLEPLUS_APP_SECRET, '',
'https://accounts.google.com/o/oauth2/auth',
'https://accounts.google.com/o/oauth2/token');
_oauth2.get('https://www.googleapis.com/plus/v1/people/me/activities/public?key={API_KEY}',
accessToken, function (err, body, res) {
if (err) {
console.log('error:');
console.log(err);
}
else console.log(body);
});
I placed the same request to get the basic user info (url: https://www.googleapis.com/oauth2/v1/userinfo) and works OK.
Can you please assist me with this?
Thank you.
If you have the access token already you should be able to use {?|&}access_token=X to get the result, example below.
var accessToken = 'XYZ';
require('http').get('https://www.googleapis.com/plus/v1/people/me/activities/public?access_token=' + accessToken, function(result){
//Use result here!
});
More information can be found within the Google+ API (Common Parameters) section

dropbox api usage in nodejs "Bad oauth_signature for oauth_signature_method"

I have been trying to connect to dropbox server and use the api, but I'm failing at the first step itself. When i'm requesting for the request token, I'm getting Bad oauth_signature error in nodejs.
The code that i'm using to connect to api is as follows.(I'm using https://github.com/sintaxi/node-dbox/blob/master/README.md library/sdk for nodejs)
/*
* dropbox handlers controller.
*/
var dbox = require('dbox')
,querystring = require("querystring")
var client = dbox.createClient({
app_key : 'my-key', // required
app_secret : 'my-secret', // required
root : 'sandbox' // optional (defaults to sandbox)
})
exports.index = function(req, res){
client.request_token(function(status, reply){
console.log(status)
console.log(reply)
// {
// oauth_token : "h89r0sdfdsfwiko", // required
// oauth_token_secret : "8hielfflk7100mv", // required
// }
})
the result i'm getting in my console is as follows
c:\tmp\dropbox>node app.js
Express server listening on port 3000 in development mode
oauth_consumer_key=[my key]&oauth_signature=faawn09ehmfe25i%2526&oauth_ti
mestamp=1324643574&oauth_nonce=132464357437334176&oauth_signature_method=PLAINTE
XT&oauth_version=1.0
403
{ '{"error": "Bad oauth_signature for oauth_signature_method \'PLAINTEXT\'"}': u
ndefined }
Any help on this is greatly appreciated.
Thanks in advance
This is the author of node-dbox. This issue has been resolved as of version 0.2.2.
Sorry for the trouble.
I took the approach of using the passport module along with its companion passport-dropbox module to handle the routes required for the authentication handshake with Dropbox. Once you receive the token and token secret passed in the Dropbox callback, store them in session state or wherever. Then you can then pass them to node-dbox in subsequent Dropbox API calls. The author of passport has a nice example on GitHub here: https://github.com/jaredhanson/passport-dropbox/tree/master/examples/login
passport.use(new DropboxStrategy({
consumerKey: DROPBOX_APP_KEY,
consumerSecret: DROPBOX_APP_SECRET,
callbackURL: APP_URL + '/auth/dropbox/callback'
},
function(token, tokenSecret, profile, done) {
var user = {
provider: 'dropbox',
displayName: profile.displayName,
email: profile.emails[0].value,
// I'm choosing to store the token and tokenSecret on the user.
// The keys must be as shown below to be compatible with node-dbox
dboxToken: {'oauth_token': token, 'oauth_token_secret': tokenSecret}
};
return done(null, user);
}));
app.get('/files', function(req, res) {
var dboxClient = dboxApp.client(req.user.dboxToken);
dboxClient.metadata('/', {}, function(status, reply) {
res.render('files', {
pathMetaData: reply,
user: req.user
});
});
});
To fix that issue you just need to apply what is mentioned here :
https://github.com/sintaxi/node-dbox/issues/3
On line 28 of Oauth.js signature is being encoded twice.
var getSignature = function(tokenSecret){
return encode(consumerSecret) + "&" + encode(tokenSecret)
}
var signature = encode(getSignature(secret))
Changing it to the following solves the problem of not being able to receive an oauth token.
var signature = getSignature(secret)
Thx

Resources