implementing passport authentication in node.js with bitly strategy - node.js

I'm trying to authenticate to bitly so I can use the link-shortener and track the users' metrics. My implementation is like this:
passport.use(new BitlyStrategy({
clientID: "my client id here",
clientSecret: "my secret here",
callbackURL: "http://website.com/auth/bitly/callback"
},
function (token, tokenSecret, profile, done) {
// Code to put it in the server here.
}
));
And the routes look like this:
app.get('/auth/bitly',
passport.authenticate('bitly'));
app.get('/auth/bitly/callback',
passport.authenticate('bitly', { failureRedirect: '/', failureFlash: true, failureFlash: 'Invalid bitly Authentication try again.' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
Now I've done everything I can think of to get this working, but I always come up with this stupid error:
Application has thrown an uncaught exception and is terminated:
TypeError: Parameter 'url' must be a string, not undefined
at Object.urlParse [as parse] (url.js:92:11)
at [object Object]._request (C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\node_modules\oauth\lib\oauth2.js:56:22)
at [object Object].get (C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\node_modules\oauth\lib\oauth2.js:196:8)
at Strategy.userProfile (C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\lib\passport-bitly\strategy.js:76:16)
at loadIt (C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\lib\passport-oauth\strategies\oauth2.js:221:17)
at Strategy._loadUserProfile (C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\lib\passport-oauth\strategies\oauth2.js:236:25)
at C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\lib\passport-oauth\strategies\oauth2.js:127:14
at C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\node_modules\oauth\lib\oauth2.js:178:7
at passBackControl (C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\node_modules\oauth\lib\oauth2.js:107:9)
at IncomingMessage.<anonymous> (C:\DWASFiles\Sites\twitter-mongo\VirtualDirectory0\site\wwwroot\node_modules\passport-bitly\node_modules\passport-oauth\node_modules\oauth\lib\oauth2.js:124:7
Anyone have any idea what that means, and where I should start to get it fixed?

I just fixed the bug in passport-bitly and made a pull request: https://github.com/dreadjr/passport-bitly/pull/1
#Bitly API: passport is a popular way to get access tokens in the node world, so it's good to have this strategy working.

We are not super familiar with the passport library over here but we recently posted some simple OAuth code examples in several languages including node.js here: http://bit.ly/bitlyoauthexamples

If you are using dreadjs's passport-bitly strategy, you will get this error. Replace the strategy.js file with that from simo's fork.
As of this date, Simo's corrections have not yet been merged into dreadjr's passport-bitly repository. I can verify that the corrections do work. Basically the original camel-cased _profileUrl should be _profileURL on line 49 of strategy.js. The correct line is:
this._profileURL = options.profileURL || 'https://api-ssl.bitly.com/v3/user/info';
There are changes made in retrieving the JSON information that are needed as well.

Related

Passport authenticate responding with HTTP 500 and does not call route methods after authentication succeeds

This is what my router method looks like.
router.get(/getData,
passport.authenticate("consumer-validation"),
getAccountData
);
I have a basic strategy defined here from passport-custom
const consumerValidationStrategy = new Strategy((req, done) => {
return done(null, { name: "John Doe" })
});
passport.use("consumer-validation", consumerValidationStrategy);
When I hit my /getData endpoint, I expect to see getAccountData being called but that doesn't happen.
I have also confirmed that the strategy is working by returning done("Error") which results in a 401 instead of 500.
Is this line exactly how you have it in your code: router.get(/getData, If so then I think it's missing quotation marks around the path value: router.get("/getData",.
Currently, you most probably have a syntax error because of the / used in the path.

While using passport-github, profile.emails is null despite user:email scope

I'm trying to get the private email of github user via passport-github.
So this is my code with scope: "user:email":
import passport from "passport";
import GithubStrategy from "passport-github";
passport.use(
new GithubStrategy(
{
clientID: process.env.GH_CLIENT_ID,
clientSecret: process.env.GH_CLIENT_SECRET,
callbackURL: `http://localhost:4000${routes.ghLoginCallback}`,
scope: "user:email",
},
async (accessToken, refreshToken, profile, cb) => {
console.log(profile.emails);
}
)
);
And now I get undefined from profile.emails.
I do get my profile correctly, but with email:null also.
Are there any possible problems that I'm missing?
yeah as #jasonandmonte mentioned, passport-github2 worked for me! Thanks a lot.
However, some people are saying that only passport-github works for them (checkout the thread that #jasonandmonte mentioned), so anyone who are in same issue in future can try both one. (Structure is almost the same.)

Endpoint 'admin/authenticated' returns 200 success code no matter what and doesn't run code in controller

I have a NodeJS back-end API for my platform built on the framework SailsJS.
When the back-end endpoint is hit that checks if the user has been authenticated, I'd like to run some extra checks and maybe some console.log()s to debug an issue I'm having.
However, when I go to do this, it looks like I cannot because no matter what code I put at the controller tied to /admin/authenticated it just returns a 200 status code with no JSON data and any code I put in the controller function does not run! This is baffling me, do not understand how that could possibly happen.
In my routes.js folder I have:
'GET /admin/authenticated': 'AdminController.isAuthenticated',
Original code at the AdminController function isAuthenticated:
isAuthenticated: function (req, res) {
return res.json(req.user);
},
Some new code I've put in there that does NOTHING:
isAuthenticated: function (req, res) {
console.log("Running isAuthenticated.");
console.log(req.user);
return res.status(400).json({error: 'Sending back 400 error'});
},
Nothing is logged from the console.log()s and a 200 success code is sent back, not the 400 error I put in there.
I'm using the Passport authentication system, and think it must have something to do with that.
Can anyone help point me in the right direction? This is mystifying.

Async response - can't set headers after they are sent

I am new to Node.js programming. I am having a big trouble as I keep getting same error in the following code. I can not understand what is the problem there.
Can't set headers after they are sent.
var mongoose = require('mongoose');
module.exports = function(req, res) {
var User = require('../../Schema/User');
User.count({ $and: [{ "username": req.body.username }, { "password": req.body.password }] },
function(err, count) {
console.log(count);
res.json(count);
});
};
You are calling next() in your route code essentially turning your login snippet into middleware and passing it down. You are getting this error because in your login code you are outputting with res.json and then still calling the next step with next. l.
Your issue is just that headers must come before any output. Whether you write or pass the data on in your middleware is up to you as long as you are aware of subsequent possible headers/writes.
But you may want to reconsider your setup - in general auth middleware would be used to verify that a user is logged on - perhaps checking a session cookie. If the user is ok then you can fetch (if needed) user data based on the cookie info (i.e. the _id) or redirect the user to a login page.
What you have here is the login route which doesnt really need middleware. Simply check if user is valid and either set a session cookie or tell them it's a no go.
Passport makes things easier. Check out this post. The underlying principle is the same I outlined above, it just makes it easier to get going.

Get the state parameters from OAuth callback in passportjs

I am trying to send state parameters to the Oauth and then catch them in the callback, but I cannot make it work. So does passportjs support such a functionality?
My idea is to send an id as state parameter to the Oauth and then on the callback depending on the id from the state parameters sent back to my app I want to do a proper redirect.
In the Twitter strategy I have enabled
passReqToCallback: true,
state: true
My request should look like this:
app.get('/auth/twitter/:gameId/:url',
function(req, res){
try {
var json = JSON.stringify({gameId: req.params.gameId, url: req.params.url});
var encodedValues = base64url(json);
console.log('encodedValues:'+encodedValues)
passport.authenticate('twitter', {state:encodedValues})
}catch (e){
console.log(e);
}
}
);
then on the callback
app.get('/auth/twitter/callback', passport.authenticate('twitter', function(req, res, next) {
try{
//get the state params from the res uri/body
//decode the state params
//redirect depending on the state.gameId
}catch(e){
console.log('twitter exception:'+e);
}}));
I already know that I can save the id in a session, but I would like to know if there is a session less way to do it by passing this information from url since it is not sensitive.
Thanks
With oAuth 2.0 you don't have to rely on any type of session for this. The way you pass in state on the /auth/twitter/:gameId/:url route, you would be able to access the state on your callback with req.query.state.
In case someone else runs into problems, when trying to pass application state through the oAuth progress, this is how I solved it in my Node-app:
Since I want to redirect the user to different locations, based on the location the user came from before entering the authentication screen, I built different routes for accessing the authentication screen, and passed different state along depending on which route was hit. Like this:
router.get('/google/io', passport.authenticate('google', {
scope: ['email', 'profile'],
state: 'io'
}));
router.get('/google/de', passport.authenticate('google', {
scope: ['email', 'profile'],
state: 'de'
}));
So if my users access the auth screen coming from my page using the .io domain, my state parameter will carry the value of 'io', and if a user accesses the auth screen coming from the .de version of my page, the state parameter will carry the value of 'de'. On successful authentication, I can then just extract the value from req.query.state, and redirect them back to mypage.de or mypage.io

Resources