facing issue with passport js - node.js

I am new to node js and I am trying to make an example for authorization with google passport below is my code:
index.js
const express = require('express');
const app = express();
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
clientSecret : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
callbackURL : "http://localhost/google/login",
passReqToCallback : true
},
function(accessToken, refreshToken, profile, done) {
return done(); //this is the issue, I am confused with it's use
}
));
app.get('/failed', function (req, res) {
res.send('failed login')
});
app.get('/final', function (req, res) {
res.send('finally google auth has done')
});
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile'] }));
app.get('/google/login',
passport.authenticate('google', { failureRedirect: '/failed' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/final');
});
app.listen('80', () => {
console.log('server is running')
})
Finally, My goal is to successful login with google without checking the value from DB as I am just learning it.
node index.js
and then i am opening url: http://localhost/auth/google
my program should run get /final after login with google credential but getting an error that TypeError: done is not a function
I am not getting the use of done() and how can I resolve it.

When you use passReqToCallback : true, you need to change the arguments of the callback function. req needs to be passed too as the first argument of the callback function.
your callback functions argument should be (req, accessToken, refreshToken, profile, done)
And that is why you are getting the error :
done is not a function
Try this :
passport.use(new GoogleStrategy({
clientID : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
clientSecret : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
callbackURL : "http://localhost/google/login",
passReqToCallback : true
},
function(req, accessToken, refreshToken, profile, done) {
return done(); // it will work now
}
));

Related

I can't reach into the verify callback when using passportjs's new OAuthStrategy() for twitter's 3-legged-auth

I would like to use Oauth1.o to get credentials from a provider but I am halfway stuck. I am using passportjs for the implementation.
Here is the brief code:
oauth.js
const passport = require('passport');
const OAuthStrategy = require('passport-oauth').OAuthStrategy;
passport.use(
'provider',
new OAuthStrategy({
requestTokenURL: "****",
accessTokenURL: "****",
userAuthorizationURL: "****",
consumerKey: "****",
consumerSecret: "****",
callbackURL: "****",
},
(token, tokenSecret, profile, done) => {
store[token] = tokenSecret;
console.log(profile);
done(null, profile);
})
);
module.exports = passport;
Express mini-app(modularised routing)
const express = require('express');
const router = express.Router();
const passport = require('./oauth');
/**
* Initialize Passport
* Restore authentication state, if any, from the session
*/
router.use(passport.initialize());
router.use(passport.session());
router.get('/oauth/sign-in', passport.authenticate('provider'));
router.get('/oauth/callback', (req, res) => {
const { oauth_token, oauth_verifier, denied } = req.query;
if (denied !== null && denied !== undefined) {
req.session.destroy();
res.json(denied);
}
res.json({ oauth_token, oauth_verifier });
});
module.exports = router;
From the above code, I successfully get to the callback URL after I authorize the app on the Twitter page. But I am unable to console.log the token, tokenSecret & profile. The documentation was also not clear to me as to how to use the done callback function.
I am on a learning journey using https://github.com/twitterdev/twauth-web/blob/master/twauth-web.py and would rather avoid using passport-twitter to learn master the example they have given.
You need to add the authenticate middleware in the callback route as well. You can check out the docs here:
router.get('/oauth/callback', passport.authenticate('provider', {
failureRedirect: '/login' //optional
}),
(req, res) => {
//...your code...
});

social login, vue-authenticate with passport(nodejs)

I'm trying to implement facebook login with vue-authenticate and passport.
I succeeded in logging into my Facebook account. And i got the 'Callback code' successfully.
This is my callback url
http://localhost:8080/auth/callback?code=AQD0FgQ7I2oci0m3bqOHOBE1EV3Ri1TBnVcqs2PRT8pFNa38NIMX-eYiSr2EiWKQBMoNq1yOeo1QkDG1OiDjF_xUduK-HWMlMazsaBzoGNxiAK3FQH5KQopZ9NUnM2g-UYLpihtpsaFrRVssJkm8Xue1nyKbbWX76EPnPCIEVOfGM_JE4mbENLpp6_w8gwkTS9n8dtsNptDM72UO9zE7mj34J8Yls0A1VqmoZail0J2zwu4hJCzAzbP2FZ531Vo2tCERn2F_4DKsJ-zq_ppZWxRlKuRW9WFBL0UvsuNN_ODiRFs70P3SoK85-xHwzHJvx8VrVxmLlp5x7rVOzy2E2Jma#=
So I used axios to pass the 'Callback code' to the server. because my server code(passport-facebook) is this:
router.route('/auth/facebook/callback').get(passport.authenticate('facebook', {
successRedirect : '/',
failureRedirect : '/'
}));
and axois code in Vue is
this.$http.get('/api/users/auth/facebook/callback',{
params:{
code : this.param
}
})
.then((response) => {
this.movies = param;
})
but it never works.. I don't know why. just wondering, i chaneged axios code to get('api/users/'). and wrote server code like this
router.get('/',()=>{
console.log("good");
});
it works. I can see the 'good' message in console.
Let me know how you implement social sign-in!
if you want to configure Facebook login with passport So you can simply follow these Steps As mention below:
Step 1. Add passport configuration
const passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
app.get('/success', (req, res) => res.send("You have successfully logged in"));
app.get('/error', (req, res) => res.send("error logging in"));
passport.serializeUser(function(user, cb) {
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
cb(null, obj);
});
Step 2. Setup facebook configuration
const FacebookStrategy = require('passport-facebook').Strategy;
const FACEBOOK_APP_ID = 'your app id';
const FACEBOOK_APP_SECRET = 'your app secret';
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
return cb(null, profile);
}
));
app.get('/auth/facebook',
passport.authenticate('facebook'));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/error' }),
function(req, res) {
res.redirect('/success');
});
This will fix your issue
Please refer this link https://www.sitepoint.com/passport-authentication-for-nodejs-applications/

Linkedin Passport Oauth failureRedirect

Good afternoon,
I'm working in a node application. Concretely I'm working with "passport-linkedin-oauth2".
There is my code.
linkedin/index.js
'use strict';
var express = require('express');
var passport = require('passport');
var auth = require('../auth.service');
var router = express.Router();
router
.get('/', passport.authenticate('linkedin', {
state: 'comienzo'
}),
function(req, res){
// The request will be redirected to Linkedin for authentication, so this
// function will not be called.
})
.get('/callback', passport.authenticate('linkedin', {
failureFlash : true,
failureRedirect: '/login'
}), auth.setTokenCookie);
module.exports = router;
linkedin/passport.js
var passport = require('passport');
var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;
var models = require('../../api');
exports.setup = function (User, config) {
passport.use(new LinkedInStrategy({
clientID: config.linkedin.clientID,
clientSecret: config.linkedin.clientSecret,
callbackURL: config.linkedin.callbackURL,
scope: [ 'r_basicprofile', 'r_emailaddress'],
state: true
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
// To keep the example simple, the user's LinkedIn profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the LinkedIn account with a user record in your database,
// and return that user instead.
return done(null, profile);
});
models.User.findOrCreate({
where: {
linkedin: profile.id
},
defaults: {
name: profile.displayName,
linkedin: profile.id,
mail: profile.emails[0].value,
password: 'xxxxxxx',
role: 'admin', provider: 'linkedin',
activo: true
}
}).spread(function (user, created) {
console.log("x: " +user.values);
return done(null, user)
}).catch(function (err) {
console.log('Error occured', err);
return done(err);
});
}
));
};
The problem I'm facing is that I'm pretty sure that LinkedIn is logging properly.
In my app when i press login button it redirect me to LinkedIn webpage, I fill the information and then my server receives this answer
GET /auth/linkedin/callback?code=AQTfvipehBLAXsvmTIl1j3ISYCzF03F-EilhiLlfSJNqiwfQsyHeslLONOWY12Br-0dfV1pgkSSpCKlmtpiMVUCufJlatEBswWqfPe6iahoRF8IHIhw&state=comienzo 302 4ms - 68b
I think that this means that it is ok because I get the state that I have sent to LinkedIn API before and the code.
Anyway, every time I login always redirect me to Login page which is failureRedirect: '/login' ( I have tested that if I change this route, the app redirect me where this attribute point)
Also I have checked that it never executes the code that search in the db for the linkedin user.
Remove the state property on the handler or at the strategy instantiation, i'm not sure why but this solves the issue.
exports.setup = function (User, config) {
passport.use(new LinkedInStrategy({
clientID: config.linkedin.clientID,
clientSecret: config.linkedin.clientSecret,
callbackURL: config.linkedin.callbackURL,
scope: [ 'r_basicprofile', 'r_emailaddress'],
state: true // <-- Remove state from here
})
}
and this code
router
.get('/', passport.authenticate('linkedin', {
state: 'comienzo' // <-- Or Remove state from here
}),
You can just set it the state on one of this places but not both, so remove one of them

Pass user info in routes from Passport strategies

I am trying to authenticate user using passportjs using express.
Passport.js looks like this.
var USER_INFO = {};
var FB_CALLBACK = 'http://localhost:3000/auth/facebook/callback';
module.exports = function(passport) {
passport.use(new FacebookStrategy({
clientID: FB_CLIENT_ID,
clientSecret: FB_CLIENT_SECRET,
callbackURL: FB_CALLBACK
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function() {
USER_INFO.id = profile.id;
});
}));
}
var express = require('express'); // call express
var app = express(); // define our app using express
var router = express.Router(); // get an instance of the express Route
var passport = require('passport');
USER_INFO = {};
require('./config/passport')(passport);
app.get('/auth/facebook', passport.authenticate('facebook'));
app.get('/auth/facebook/callback', passport.authenticate('facebook', {
successRedirect : '/fb',
failureRedirect : '/error'
}));
app.get('/fb', function (req, res) {
res.json(USER_INFO);
});
I want all the information extracted in res.json(user_info). But it is coming as empty. What I am missing here. What is the best method to save user basic info of user to keep him logged in.
Firstly, you should not store USER_INFO = {} outside the scope of your current request. If two separate users make a request then they'll get the same object.
You should at least store them in a way you can retrieve them separately
var USERS = {};
...
module.exports...
passport.use...
...
function(accessToken, refreshToken, profile, done) {
USERS[profile.id] = profile;
done(null, profile);
}));
Now if two separate users make a request they'll have their info separately stored within USERS
{
1234: {id: 1234, name: FOO},
6789: {id: 6789, name: BAR},
}
And done(null, profile) would serialize that user. If you haven't defined your serialize/deserialize functions you should do so like this:
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
var user = USERS[id];
done(null, user);
});
Now your users will be available within their respective request contexts as req.user
So you just need to do:
app.get('/fb', function (req, res) {
res.json(req.user);
});
You forgot call done() in process.nextTick().
var FB_CALLBACK = 'http://localhost:3000/auth/facebook/callback';
module.exports = function(passport) {
passport.use(new FacebookStrategy({
clientID: FB_CLIENT_ID,
clientSecret: FB_CLIENT_SECRET,
callbackURL: FB_CALLBACK
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function() {
var USER_INFO = {};
USER_INFO.id = profile.id;
done(USER_INFO)
});
}));
}
You can pass any object to done(), it will become req.user later in your route. In your case, the USER_INFO which you want to response is req.user
app.get('/fb', function (req, res) {
res.json(req.user);
});

Can the callback for facebook-pasport be dynamically constructed?

When using facebook-passport the usual thing to do is to specify the redirect_uri in the constructor of the FacebookStrategy thst you use, something like this:
passport.use("facebook", new FacebookStrategy({
//TODO: Correctly configure me
clientID: "XXXXXXX"
, clientSecret: "XXXXXXXXX"
, callbackURL: "http://localhost:3007/auth/facebook/callback"
},
function(accessToken,refreshToken,profile,done) {
User.findByFacebookId(profile.id, function(err,user) {
if(err){ return done(err);}
if(!user){ return done(null,false)}
return done(null, user);
});
})
);
Then you would set up routes like this:
app.get('/auth/facebook/login', passport.authenticate('facebook') );
app.get('/auth/facebook/login_callback', passport.authenticate('facebook', {
successRedirect:"/login_ok.html"
, failureRedirect:"/login_failed.html"
}
))
Is it possible to change the callback url so that it contains information from parameters passed to the initial login call?
NOTE: This question is more for preserving info that took me a while to work out, to avoid others going down the same paths.
I found the answer using some info found here https://github.com/jaredhanson/passport-facebook/issues/2 and through digging through the way the passport oauth2 component determines callback uris, and information about passport custom callbacks at the bottom of this page http://passportjs.org/guide/authenticate/.
Here's an example that maps calls to /auth/facebook/login/1234 to use the callback /auth/facebook/login_callback/1234
app.get('/auth/facebook/login/:id', function(req,res,next) {
passport.authenticate(
'facebook',
{callbackURL: '/auth/facebook/login_callback/'+req.params.id }
)(req,res,next);
});
app.get('/auth/facebook/login_callback/:id', function(req,res,next) {
passport.authenticate(
'facebook',
{
callbackURL:"/auth/facebook/login_callback/"+req.params.id
, successRedirect:"/login_ok.html"
, failureRedirect:"/login_failed.html"
}
) (req,res,next);
});
#OMGPOP, here you can pass in query params into your callbackUrl.
var Passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
const Router = require("express").Router();
var fbConfig = {
display: "popup",
clientID: "YourFbClientId",
clientSecret: "YourFbClientSecret",
callbackURL: "http://localhost:8686/auth/facebook/callback",
profileFields: ['id', 'name', 'gender', 'displayName', 'photos', 'profileUrl', 'email']
}
Passport.use(new FacebookStrategy(fbConfig,
function(accessToken, refreshToken, profile, callback) {
return callback(null, accessToken);
}
));
Router.get("/auth/facebook", function(req, res, next) {
var callbackURL = fbConfig.callbackURL + "?queryParams=" + req.query.queryParams;
Passport.authenticate("facebook", { scope : ["email"], callbackURL: callbackURL })(req, res, next);
});
Router.get("/auth/facebook/callback", function(req, res, next) {
Passport.authenticate("facebook", {
callbackURL: fbConfig.callbackURL + "?queryParams=" + req.query.queryParams,
failureRedirect: "/login",
session: false
})(req, res, next) },
function(req, res) {
console.log(req.query.queryParams);
//do whatever you want
});
Check out my blog for more information: http://blog.pingzhang.io/javascript/2016/09/22/passport-facebook/
I was struggling to do this specifically with Angularjs, and wanted to redirect back to the same url that the login was initiated from.
My solution was to create a route in Angularjs that just implements a location back. I know this does not specifically answer the question, but I thought it would be helpful for anyone looking to do the same.
On the server:
app.get('/auth/facebook/', passport.authenticate ('facebook'));
app.get('/auth/facebook/callback', function (req, res, next) {
var authenticator = passport.authenticate ('facebook', {
successRedirect: '/fbcallback',
failureRedirect: '/'
});
delete req.session.returnTo;
authenticator (req, res, next);
})
Angular router:
when('/fbcallback', {
template: "",
controller: 'fbCtrl'
}).
Angular controller:
app.controller("fbCtrl", function () {
window.history.back();
});
You could probably do some other client side routing in the controller as well.

Resources