I'm using passport-linkedin to integrate LinkedIn account in my project. Problem is when linkedin email does not found in my database, I need to display linkedin account information in callback function.
passport.js
passport.use(new LinkedInStrategy({
consumerKey: '12121',
consumerSecret: '1212121',
callbackURL: "/auth/linkedin/callback",
profileFields: ['id', 'first-name', 'last-name', 'email-address', 'headline']
},
function(token, tokenSecret, profile, done) {
auth.findOne({ username: profile.emails[0].value }).then(function (err, user) {
if (!user) {
return done(null, profile);
} else {
return done(null, user);
}
}, function (err) {
return done(err, null);
})
}
));
routes.js
app.get('/auth/linkedin/callback',
passport.authenticate('linkedin', { failureRedirect: '/login' }),
function(req, res) {
winston.error('linkedInfo: %s', req);
res.redirect('/');
});
In routes.js, I want to display all of json data from LinkedIn. But nothing display as not working at all.
One thing to check would be ensuring that you are requesting the r_basicprofile and r_emailaddress member permissions during the OAuth process, if you want to return a member's email address.
The entire call could be failing (and obscured to you via the Passport layer) as a result of you not having the necessary permissions to request all of the fields you are asking for.
Related
I've been taking courses and watching tutorials on NodeJS for awhile and decided to put them to good use in an app.
For this project I need users to signup and login in order to store their activity in a database. I used Passport to do this process, the code for this section of the project is this:
/****** Passport functions ******/
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
db.user.findOne( { where : { idUser : id } }).then(function (err, user) {
done(err, user);
});
});
//Facebook
passport.use(new FacebookStrategy({
//Information stored on config/auth.js
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: configAuth.facebookAuth.callbackURL,
profileFields: ['id', 'emails', 'displayName', 'name', 'gender']
}, function (accessToken, refreshToken, profile, done) {
//Using next tick to take advantage of async properties
process.nextTick(function () {
db.user.findOne( { where : { idUser : profile.id } }).then(function (err, user) {
if(err) {
return done(err);
}
if(user) {
return done(null, user);
} else {
db.user.create({
idUser : profile.id,
token : accessToken,
nameUser : profile.displayName,
email : profile.emails[0].value,
sex : profile.gender
});
return done(null);
}
});
});
}));
app.use(express.static(__dirname + '/public/'));
/* FACEBOOK STRATEGY */
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback//
app.get('/auth/facebook', passport.authenticate('facebook', { scope : ['email']}));
/* FACEBOOK STRATEGY */
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { successRedirect: '/app',
failureRedirect: '/' }));
app.get('/', function (req, res) {
res.render('/');
});
app.get('/app', isLoggedIn, function (req, res) {
res.sendFile('app.html');
});
function isLoggedIn(req, res, next) {
if(req.isAuthenticated()) {
return next();
} else {
res.redirect('/');
}
}
The tutorial I followed on Facebook Auth using Passport used pretty much the same code, I changed the User model because the tutorial used Mongoose and I'm using Sequelize but this aspect is working great, when I click to signup with FB it registers me or logs me in, the queries do the work.
However, what isn't working is the redirection. When I register using facebook, it gets stuck and doesn't load anything (wheel keeps spinning on index.html (where the FB button is) and doesn't load anything). When I login using facebook, it only displays this on the screen:
[object SequelizeInstance:user]
On the tutorial, the instructor used EJS as a template language,however I already built 95% of the front end of the project using HTML, CSS and jQuery (yeah, should have used React or Angular but time is sensitive and was already learning Node). I believe this is one of the reasons this is happening but I'm not 100% sure on what is going on here and why I'm getting the error or how to get around.
Any help is appreciated, if more information / code is needed, let me know. Thank you
So after a good amount of time debugging and with some good help, I figured out what was causing my problem, there were actually three errors in there.
First of all, in the Facebook Strategy, this is how I should had built it:
passport.use(new FacebookStrategy({
//Information stored on config/auth.js
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: configAuth.facebookAuth.callbackURL,
profileFields: ['id', 'emails', 'displayName', 'name', 'gender']
}, function (accessToken, refreshToken, profile, done) {
//Using next tick to take advantage of async properties
process.nextTick(function () {
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(err) {
return done(err);
}
if(user) {
return done(null, user);
} else {
//Create the user
db.user.create({
idUser : profile.id,
token : accessToken,
nameUser : profile.displayName,
email : profile.emails[0].value,
sex : profile.gender
});
//Find the user (therefore checking if it was indeed created) and return it
db.user.findOne( { where : { idUser : profile.id } }).then(function (user, err) {
if(user) {
return done(null, user);
} else {
return done(err);
}
});
}
});
});
}));
The callback after db.user.findOne had switched parameters, so it was giving me an error every time even though it didn't have one, so I switched those and also added a query to look for the user in the DB after creating it to be able to return it.
On the second facebook route, this is how I built it:
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('../../app.html');
});
This allowed me to continue using HTML (I'll probably rewrite it to use a better view later on), and on testing, I was able to get the information from req.user.
Finally, I had a minor naming error on Passport's serializeUser:
passport.serializeUser(function (user, done) {
done(null, user.idUser);
});
Just changing from user.id to user.idUser to maintain the naming convention I used.
Hopefully this helps other people using Sequelize with Passport.
I'm working with nodejs+express+mongo and angularjs(MEAN). I'm login in with Facebook strategy for passport but I can't seem to find the way to access the user info(req.user) from my angular client.
It should be noted that the angular front is on another app and web server, apart from the node express mongo side.
This is my nodejs code:
passport.serializeUser(function(user, done){
done(null, user._id);
});
passport.deserializeUser(function(id, done){
UserModel.findById(id, function(err, user){
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: config.facebook.appId,
clientSecret: config.facebook.appSecret,
callbackURL: config.facebook.redirectUri,
scope: config.facebook.scope,
profileFields: ['name','displayName','gender','profileUrl','email']
},
function(accessToken, refreshToken, profile, done) {
UserModel.findOne( {$or:[{'facebook_profile.id': profile.id},{'email':profile.emails[0].value}]}, function(err, user){
var newuser = new UserModel();
if(err){
return done(err);
}
if(user){
return done(null, user);
}
else {
newuser.facebook_profile.id = profile.id;
newuser.facebook_profile.name = profile.displayName
return done(null, newuser);
}
});
})
);
app.get('/auth/is_auth', function(req, res){
if (req.isAuthenticated()){
res.json(req.user);
}
})
After this, from Postman I call
http://localhost:3000/auth/is_auth
But req.isAuthenticated() is always FALSE
What can it be, I can't never get the user info
req.isAuthenticated() is false, if you use CORS and in your ajax request you didn't set withCredentials to true. Be sure withCredentials is set to true.
I would like to save the user object within the request object returned from facebook after successful authentication using passport to use in a different method. The purpose for this is to include in my linkschema all different users who post via the same link.
The flow should be as follows:
facebook authenticate
user object is stored somewhere [HOW DO I DO THIS PART???]
link is clicked, gets routed to post method whose parameters are the link and userid.
(If the link exists, the users are appended to the array of users defined in my linkschema)
/ =====================================
/ / FACEBOOK ROUTES
// =====================================
// route for facebook authentication and login
passport.use(new FacebookStrategy({
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: "http://localhost:3000/auth/facebook/callback/"
},
function(accessToken, refreshToken, profile, done) {
UserSchema.AddUnique(profile, accessToken, function(err, user) {
if (err) {
return done(err);
}
return done(null, user);
});
}
));
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback
router.get('/auth/facebook', passport.authenticate('facebook', {
scope: 'email'
}));
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
var user = router.get('/auth/facebook/callback',
passport.authenticate('facebook', {
failureRedirect: '/login'
}), function(req, res) {
var user = req.user;
res.redirect('/browse');
return function() {
return user;
}();
});
function user() {
console.log('in here');
console.log(user);
}
router.use(function(err, req, res, next) {
console.log(err)
next(err)
});
router.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
Thank you in advance!
Store them outside in an object
var Users = {};
passport.use(new FacebookStrategy({…},
function(accessToken, refreshToken, profile, done) {
UserSchema.AddUnique(profile, accessToken, function(err, user) {
if (err) {
return done(err);
}
// Push the user into that object
Users[user.id] = user;
return done(null, user);
});
}
));
function user() {
console.log(Users) //=> { '5234…': {…} , '5345…': {…} , … }
}
I am trying to make a node.js web application that tells the user to sign in using their gmail.
So I tried to use the instructions over here: http://passportjs.org/guide/google/. I changed the url www.example.com to localhost, then ran the application. It tells me that it can't find User. Here is the whole log: User.findOrCreate({openID: identifier }, function(err, user) {(and then on the next line) ReferenceError: User is not defined.
You need to define "User" by calling it from a model. Create a User model (if you haven't already) and import it as a variable. E.g.
var User = require('/path/to/User');
Sometimes I find it helpful for debugging to log the callback to the console, to see if the desired output is being spit out.
I just implemented one maybe this will help , I'm using Express the routes section is on the bottom.. Remember to set your host in the Google Key, my App has de full url of the AWS Server
var passport = require('passport');
// ====== Passport and OAuth2 API
var GoogleStretegy = require('passport-google-oauth').OAuth2Strategy;
passport.serializeUser(function (user, done) {
done(null, user);});
passport.deserializeUser(function (obj, done){
done(null, obj);});
// Set Passport Initialize and Sessions
app.use(passport.initialize());
app.use(passport.session());
passport.use(new GoogleStretegy({
clientID: CREDENTIALS.google.GOOGLE_CLIENT_ID,
clientSecret: CREDENTIALS.google.GOOGLE_CLIENT_SECRET,
callbackURL:"<host>/oauth2callback"
},
function (req, accessToken, refreshToken, profile, done) {
process.nextTick(function () {
console.log(JSON.stringify(profile));
console.log(req);
var username= profile.emails[0].value.split('#');
User.findOne({email: profile.emails[0].value,username:username[0]}).exec(function (err,user) {
if(!user){
var user = new User({username: username[0]});
user.set('email',profile.emails[0].value);
user.set('FullName',profile.DisplayName);
user.save(function (err) {
if(err){
console.log(err);
profile=null;
return done(null,profile);
}else {
return done(null, profile);
}
});
}else {
return done(null, profile);
}
});
// return done(null, profile);
});
}
));
/// ROUTES !
router.get('/logout', function (req, res) {
req.session.destroy(function () {
// Google log out
req.logout();
res.redirect('/login');
});
});
//Google OAuth2
router.get('/auth/google',passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/userinfo.email'] }));
router.get('/oauth2callback', passport.authenticate('google', { failureRedirect: '/login' }), function (req, res) {
res.redirect('/');
});
I've got a node app that operates in isolation. Without js on in the client it just operates synchronously, setting cookies with passport.js. When the client is js enabled then the auth is done via a rest route.
All seems fine, except that if I have authenticated (and have cookies set) asynchronously, but then refresh the page (or navigate synchronously to a new one [for whatever reason]), the server sends the response with new cookies that overwrite the old ones and setting the user back to being unauthenticated.
// =============================================================================
// AUTHENTICATE (FIRST LOGIN) ==================================================
// =============================================================================
// process the login form
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/profile', // redirect to the secure profile section
failureRedirect: '/browse?p=0', // redirect back to the signup page if there is an error
failureFlash: true // allow flash messages
}));
app.post('/async/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info, status) {
if (err) {
return res.send(err);
}
if (user) {
user.local = null;
return res.send(user);
} else {
return res.send(info);
}
})(req, res, next);
});
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
// asynchronous
process.nextTick(function() {
User.findOne({
'local.email': email
}).populate({
path: 'spots comments currentLocation'
}).exec(function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found.'));
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
// all is well, return user
else
User.populate(user, {
path: 'spots.bird',
model: 'Bird'
}, function(err, user) {
if (err)
return done(err);
else
User.populate(user, {
path: 'spots.location',
model: 'Location'
}, function(err, user) {
if (err)
return done(err);
else
console.log(util.inspect(user, showHidden = false, depth = 5, colorize = true));
return done(null, user);
});
});
});
});
}));
Node App:
app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(cookieParser());
sessionVars = {
keys: ['<removed>'],
//secureProxy: true, // if you do SSL outside of node
maxAge: 2592000000
};
app.use(session(sessionVars));
app.use(flash());
require('./../modules/auth/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
Does anyone know why?
UPDATE: The async route doesn't seem to call the serializeUser function (which explains everything!). Does anyone know how to force serialization?
If I try a direct call: passport.serializeUser(user, done) then I need the done parameter, which isn't available in the routes file (although I could pass it through);
Does anybody know if serializeUser is meant to be called as a matter of course with passport strategies?
So I found that if you use the custom form of the authenticate function:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
it doesn't seem to pass it through the serializeUser function that you provide. The cookies for sessions stay in your browser and change all the time, even when you're logged out as they probably still 'tracking' your behaviour.
If you use the 'black box' version of the function:
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
that works because it calls serializeUser automatically.