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.
Related
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.
I'm implementing a server that handles chat messages. In some cases I want to access data from a JIRA instance. I'm using passport-atlassian-oauth strategy for authenticating with JIRA and BearerStrategy for requests, but my issue is that the authentication is only valid in the browser after a user has given "My Server" read and write access to JIRA. In many guides they just call res.redirect('/successfulLogin') or something similar after a successful authentication, but I would instead like to do a rest call to JIRA, process the data and send it to my connected client application.
How do I do that?
I'm completely new to all this and everything just spins around in my head. I save and have access to the token used for authentication and when I for instance navigate to .../test/access_token=?[token] in my browser it works.
passport.use(new BearerStrategy(
function(token, done) {
// Find user by token
client.smembers('access_token:' + token, function(err, replies) {
if (err) {
return done(err);
}
// if user found
// TODO: yet again, hard coded for one
if (replies.length > 0) {
console.log('SHOULD BE 1:', replies[0]);
client.hgetall('users:' + replies[0], function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
return done(null, user, {scope: 'all'});
});
}
});
}
));
As you can see it's hard coded for just one user and I'm using Redis as a "database".
passport.use(new AtlassianOAuthStrategy({
applicationURL: 'http://localhost:2990/jira',
callbackURL: '/auth/atlassian-oauth/callback',
consumerKey: RsaPublicKey,
consumerSecret: rsaPrivateKey,
clientId: 'MyBot'
},
function(accessToken, tokenSecret, profile, done) {
// Find user
client.hgetall('users:1', function(err, user) {
if(err) {
return done(err);
}
// user not found
if(!user) {
// create new user, no worries!
// TODO: HARD CODED FOR ONE USER
client.hmset('users:1', 'id', profile.id, 'access_token', accessToken, function(err, res) {
client.sadd('id:admin', '1');
client.sadd('access_token:'+ accessToken, '1');
client.hgetall(profile.id, function(err, user) {
return done(null, user);
});
});
} else {
// Update access token!
client.hmset(profile.id, 'access_token', accessToken, function() {
client.sadd('access_token:' + accessToken, '1', function() {
client.hgetall(profile.id, function(err, result) {
return done(null, user);
});
});
});
}
});
}
));
Here's the rest
app.get('/auth/atlassian-oauth',
passport.authenticate('atlassian-oauth', {session: false, scope: []}),
function(req, res) {
console.log('- Function: /auth/atlassian-oauth - should not be called)');
});
app.get('/auth/atlassian-oauth/callback',
passport.authenticate('atlassian-oauth', {session: false, failureRedirect: '/login'}),
function(req, res) {
console.log('- Function: /auth/atlassian-oauth/callback - Authentication successful!', req.user.access_token);
// Update access token!
// Should I even do this? Shouldn't I already have the correct token?
client.hmset('users:1', 'access_token', req.user.access_token, function() {
client.sadd('access_token:' + req.user.access_token, '1', function() {
client.hgetall('users:1', function(err, result) {
res.redirect('/test?access_token=' + req.user.access_token);
});
});
});
});
So now that you've seen some relevant (just tell me and I'll post more) code, how do I send a rest call to JIRA without getting a 401? :)
EDIT: Any help appreciated! You would make me really happy if you just can point me into the right direction!
Ok. I figured it out! First of all you want to save both you access token and token secret to you db in AtlassianOAuthStrategy. Second, in order to send a REST call to a third party service you can just use http request with OAuth:
var request = require('request');
var oauth = {
signature_method : 'RSA-SHA1',
consumer_key : RsaPublicKey,
private_key : rsaPrivateKey,
token : [get access_token from you db],
token_secret : [get token_secret from you db]'
};
var url = 'http://localhost:2990/jira/rest/api/2/issue/' + id;
request.get({url:url, oauth:oauth, json:true}, function (e, r, issue) {
console.log(issue)
});
Now that everything is working I'm going to start refactoring and reading some more documentation in order to make the design prettier and figure out how to use Redis properly :)
The below custom call back for passport.js doesn't seems to work, no mater what i do.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, users, info) {
console.log(users);
if (user === false) {
console.log('Failed!');
} else {
res.redirect('/');
}
})(req, res, next);
});
The same if i change it to like below all works as expected.
app.post("/login"
,passport.authenticate('local',{
successRedirect : "/",
failureRedirect : "/login",
})
);
Also I've noticed when using custom callback even the passport.serializeUser and passport.deserializeUser also not getting invoked by passport.js.
Is this any sort of a bug or am i doing something wrong here ??
My Local-Strategy:
passport.use('local-sigin',new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
console.log('Passport Strategy Sign in:');
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done({status:'ERROR',message:'Something went wrong!'});
// if no user is found, return the message
if (!user)
return done({status:'ERROR',message:'No user found.'}, false);
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done({status:'ERROR',message:'Oops! Wrong password.'}, false);
// all is well, return successful user
return done({status:'OK',message:'Login success.'}, user);
});
}));
I am guessing that by 'doesn't work' you mean to say that the user is never being logged in.
Firstly, your local strategy is named 'local-sigin' however on a POST to '/login' you are invoking the 'local' strategy, which presumably doesn't exist:
passport.use('local', new LocalStrategy({
Change the name of your strategy to be consistent (or vice versa!):
passport.authenticate('local'
Secondly, your 'local' authentication callback has a parameter users (plural) but you are trying to access user (singular) within its body, meaning user is undefined and user === false is false under strict equality:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
// ^^^^
console.log(user);
if (!user) {
console.log('Failed!');
} else {
res.redirect('/');
}
})(req, res, next);
});
And finally, you are never logging the user in when authentication is successful. Creating a session for a user is not automatic, you must call req#login:
Passport exposes a login() function on req (also aliased as logIn()) that can be used to establish a login session.
Let's add that to your authentication callback:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
console.log(user);
if (!user) {
console.log('Failed!');
} else {
req.login(user, function (err) {
if(err) {
console.log(err);
return;
}
res.redirect('/');
});
}
})(req, res, next);
});
Take a look at the Passport docs, they explain in a good amount of detail how these processes work and how to implement them.
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('/');
});