Nodejs express integration authentification local and facebook - node.js

I actually develop a restfull server using NodeJS and express.
I've already implemented oauth2 protocol in order to secure my API.
I want to add Facebook authentication but I don't manage to integrate the facebook authentification with my local authentication.
server.js
router.route('/oauth/local')
.post(oauth2Controller.token);
router.route('/oauth/facebook')
.get(passport.authenticate('facebook-token'), oauth2Controller.token);
//impossible to call oauth2Controller.token
oauth2.js
var oauth2orize = require('oauth2orize')
var User = require('../models/user');
var AccessToken = require('../models/accesstoken');
var RefreshToken = require('../models/refreshtoken');
var passport = require('passport');
var server = oauth2orize.createServer();
server.exchange(oauth2orize.exchange.password(function(application, username, password, scope, callback){
User.findOne({username: username}, function(err, user){
if (err)
return (callback(err));
if (!user)
return (callback(null, false));
user.verifyPassword(password, function(err, isMatch){
if (err)
return (callback(err));
if (!isMatch)
return (callback(null, false));
var accessToken = new AccessToken({
user: user.username,
application: application.oauth_id,
scope: '*'
});
accessToken.save(function(err){
if (err)
return (callback(err));
});
var refreshToken = new RefreshToken({
user: user.username,
application: application.oauth_id
});
refreshToken.save(function(err){
if (err)
return (callback(err));
});
callback(null, accessToken.value, refreshToken.value);
});
})
}));
server.exchange(oauth2orize.exchange.refreshToken(function(application, refreshToken, scope, callback){
RefreshToken.findOne({value: refreshToken}, function(err, token){
if (err)
return (callback(err));
if (!token)
return (callback(null, false));
var accessToken = new AccessToken({
user: token.user,
application: application.oauth_id,
scope: '*'
});
accessToken.save(function(err){
if (err)
return (callback(err));
});
callback(null, accessToken.value, refreshToken.value);
});
}));
exports.token = [
passport.authenticate(['clientBasic'], {session: false}),
server.token(),
server.errorHandler()
];
auth.js
passport.use(new FacebookTokenStrategy({
clientID: ID_CLIENT_FACEBOOK,
clientSecret: SECRET_CLIENT_FACEBOOK
},
function(accessToken, refreshToken, profile, done) {
//Do something here
done(null, profile);
}));
The local authentication and facebook authentication work fine.
But after be authenticated with facebook, I don't manage to give the needed informations to oauth.token in order to obtain tokens for my API.

after authenticate with face book store token value inside the db.
ask the oAuth server for refresh token
in refresh token you can store scope information

Related

Express Passport Google Strategy - using res.redirect

I have an express app which manages authentication via Passport with support for both local and Google strategies.
At the moment I support account creation via Google auth but actually a user's Google account doesn't contain all the info that my application requires. Because of this I would like to redirect uses who don't have a local account to my own account creation and only support account linking (i.e. their local account can be linked to Google account for authorization).
The problem is that I would normally do this via res.redirect but res is not available in the Google Strategy - what is the best way to handle this?
In 'index.js' where I define my routes I define const passportGoogle = require('../handlers/google'); which has the details of my Google Strategy.
Further down in index.js I have my authenticate and authorise routes:
/* GOOGLE ROUTES FOR AUTHENTICATION*/
router.get('/google',
passportGoogle.authenticate('google',
{ scope: ['profile', 'email'] }));
router.get('/google/callback',
passportGoogle.authenticate('google',
{
failureRedirect: '/',
failureFlash: 'Failed Login!',
successRedirect: '/account',
successFlash: 'You are logged in!'
}
));
/* GOOGLE ROUTES FOR AUTHORISATION - IE A USER IS ALREADY LOGGED IN AND WANTS TO CONNECT THEIR GOOGLE ACCOUNT*/
// send to google to do the authentication
router.get('/connect/google',
passportGoogle.authorize('google',
{ scope : ['profile', 'email'] }
));
// the callback after google has authorized the user
router.get('/connect/google/callback',
passportGoogle.authorize('google', {
successRedirect : '/profile',
failureRedirect : '/'
})
);
As above my Google strategy is defined in google.js:
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var User = require('../models/User');
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENTID,
clientSecret: process.env.GOOGLE_CLIENTSECRET,
callbackURL: "http://127.0.0.1:7777/google/callback",
passReqToCallback: true
},
// google will send back the token and profile
function(req, accessToken, refreshToken, profile, done) {
// asynchronous
process.nextTick(function() {
// check if the user is already logged in
if (!req.user) {
// find the user in the database based on their facebook id
User.findOne({ 'google.id': profile.id }, function(err, user) {
// if there is an error, stop everything and return that
// ie an error connecting to the database
if (err)
return done(err);
// if the user is found, then log them in
if (user) {
return done(null, user); // user found, return that user
} else { <--here is where I want to redirect
//currently I create a new user but i really want to redirect them to create a local account
// if there is no user found with that google id, create them
var newUser = new User();
// set all of the facebook information in our user model
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.name = profile.displayName;
newUser.email = profile.emails[0].value;
// save our user to the database
newUser.save(function(err) {
if (err)
throw err;
// if successful, return the new user
return done(null, newUser);
});
}
});
} else {
const user = User.findOneAndUpdate(
{ _id: req.user._id },
{ $set: {"user.google.id":profile.id,
"user.google.token":accessToken,
"user.google.name":profile.displayName,
"user.google.email":profile.emails[0].value
}
},
{ new: true, runValidators: true, context: 'query' }
)
.exec();
return done(null, user);
req.flash('success', 'Google details have been added to your account');
res.redirect(`back`);
}
});
}));
module.exports = passport;
Any help much appreciated!

how to send user profile data on success redirect with passport?

I'm quite new to Node.js, and i'm learning authentication with passport, in my node application i have following lines which authenticates user with google and fires a callback after successful authentication,
// send to google to do the authentication
app.get('/auth/google', passport.authenticate('google', { scope : ['profile', 'email'] }));
// the callback after google has authenticated the user
app.get('/auth/oauth/callback',passport.authenticate('google', {successRedirect : 'http://127.0.0.1:1234/testbot',failureRedirect : '/'}));
i want to send the user profile data to the page registered with successRedirect, how can i do that?
my passport strategy for authentication with google is like so
passport.use(new GoogleStrategy({
clientID : configAuth.googleAuth.clientID,
clientSecret : configAuth.googleAuth.clientSecret,
callbackURL : configAuth.googleAuth.callbackURL,
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, token, refreshToken, profile, done) {
console.log("Its here ");
// asynchronous
process.nextTick(function() {
// check if the user is already logged in
if (!req.user) {
User.findOne({ 'UserName' : req.body.email }, function(err, user) {
if (err)
return done(err);
if (user) {
// if there is a user id already but no token (user was linked at one point and then removed)
if (!user.google.token) {
user.google.token = token;
user.google.name = profile.displayName;
user.google.email = (profile.emails[0].value || '').toLowerCase(); // pull the first email
user.save(function(err) {
if (err)
return done(err);
return done(null, user);
});
}
return done(null, user);
} else {
var newUser = new User();
newUser.UserName= (profile.emails[0].value || '').toLowerCase();
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.google.name = profile.displayName;
newUser.google.email = (profile.emails[0].value || '').toLowerCase(); // pull the first email
newUser.save(function(err) {
if (err)
return done(err);
return done(null, newUser);
});
}
});
} else {
// user already exists and is logged in, we have to link accounts
var user = req.user; // pull the user out of the session
user.google.id = profile.id;
user.google.token = token;
user.google.name = profile.displayName;
user.google.email = (profile.emails[0].value || '').toLowerCase(); // pull the first email
user.save(function(err) {
if (err)
return done(err);
return done(null, user);
});
}
});
}));
Im not sure what front end your using but if its React with Redux and if you have SSR you can preloadState with the req.user info by firing an action from your server. Here's documentation on that.
Or if you have session's setup you can make a get request when your front end loads and respond back with the req.user info.

Node / Angular / Passport / Satellizer Token - Based Facebook Authentication not returning token

I am using Satellizer on Angular with a Node backend doing authentication with Passport.js. I have implemented the login flow according to the Satellizer documentation and am able to return a token to the browser through my Passport / Node server. However, when I return the token, it is returned to the popout window, not the Angular application itself. As a result, I receive this error in the angular application after the popout window self-closes:
Expecting a token named "token
Here are my passport routes:
app.post('/login/facebook', passport.authenticate('facebook-login', { 'scope': ['email'] }))
app.get('/login/facebook-callback', passport.authenticate('facebook-login'), function(req, res) { tokenHelper.createSendToken(req.user, res); })
And here is my Passport Facebook strategy:
var facebookLoginStrategy = new FacebookStrategy({
clientID: process.env.FACEBOOK_APP_ID,
clientSecret: process.env.FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:8080/login/facebook-callback",
passReqToCallback: true,
profileFields: ['email']
}, function(req, accessToken, refreshToken, profile, done) {
if (!req.user) {
User.findOne({
"facebookData.id": profile.id
}, function(err, user) {
if (err) return done(err);
if (user) {
// Authenticate
console.log('found user')
return done(null, user);
} else {
// Register
console.log('making new user')
var newUser = new User();
newUser.facebookData = profile
newUser.email = profile.emails[0].value
newUser.username = profile.emails[0].value
console.log(newUser)
newUser.save(function(err) {
if (err) throw err;
return done(null, newUser);
});
}
});
} else {
// Authorize
console.log('updating user')
var user = req.user
user.facebookData = profile;
user.save(function(err) {
if (err) throw err;
return done(null, user);
});
}
})
passport.use('facebook-login', facebookLoginStrategy)
I installed passport session even though I'm using tokens, but it still returns the token to the wrong place. How can I make Satellizer pass the token it receives in the popout window forward to the angular application? Or do I need to modify my authentication flow to accommodate these frameworks? Thanks for your help!

Using the returned token to access github api

I used passportjs and passport-github to create a social login in my application,
passport.use(new GithubStrategy(
{
clientID : configAuth.githubAuth.clientID,
clientSecret : configAuth.githubAuth.clientSecret,
callbackURL : configAuth.githubAuth.callbackURL
},
function(token, refreshToken, profile, done) {
process.nextTick(function() {
User.findOne({'github.id' : profile.id}, function(err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, user);
} else {
var newUser = new User();
newUser.github.id = profile.id,
newUser.token = token,
newUser.name = profile.displayName;
newUser.email = profile.emails[0].value;
newUser.username = profile.username;
// save
newUser.save(function(err){
if (err) {
throw err;
}
return done(null, newUser);
});
}
});
});
}
));
Now I am using another component called octonode, which requires a access_token to authenticate its user, was the token in the callback the same as this access_token, because I do not seem like authenticated when doing this:
var github = require('octonode');
exports.read = function (req, res, next) {
var client = github.client();
client.get('/user?access_token=' + req.user.token, {}, function (err, status, body, headers) {
res.json(body);
});
};
And also tried doing this:
var client = github.client(req.user.token);
client.get('/user',{}, function...)
I get a blank screen, meaning no response.
Alright, as one answer in SO states that:
Note that Passport does not actively use the access token or refresh token, other than to fetch the user profile during login. You're application is responsible for using these tokens when making whatever API requests are necessary. As such, you can implement either method you describe, Passport is not involved in the process.
Jared Hanson
The access_tokens are returned to you, but it does not handle it after, you are the one responsible to save it or to do whatever you want.
My code is basically inspired by a tutorial in Scotch.io's facebook auth using passport. There they do not update the token every login, because they need not in their tutorial, but they do save it in the database, check their source code
With few a few comments, and debugging, I found that that is the culprit in my application, so I need to update the condition that states if a user is found, update the token, and some values so some important info will persists on login.
if (user) {
user.token = token;
user.name = profile.displayName;
user.email = profile.emails[0].value;
user.save();
return done(null, user);
}
And now this will work out just fine:
var client = github.client(req.user.token);
client.get('/user', {}, function (err, status, body, headers) {
res.json(body);
});
Thanks to #MikeSmithDev for helping me out.

sails.js + passport.js : managing sessions

I am trying to implement a facebook connection in sails using passport. Therefore, I've created a passport.js file in my services folder, the code is given below. It looks like the login is done successfully, however the user serialization doesn't seem to work as the console.log that I put in it never appears in the console and I cannot access the user id trhough req.user once the user is supposed to be logged in. Did anyone managed to get passport working with sails?
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy,
bcrypt = require('bcrypt');
// helper functions
function findById(id, fn) {
User.findOne(id).done( function(err, user){
if (err){
return fn(null, null);
}else{
return fn(null, user);
}
});
}
function findByUsername(u, fn) {
User.findOne({
username: u
}).done(function(err, user) {
// Error handling
if (err) {
return fn(null, null);
// The User was found successfully!
}else{
return fn(null, user);
}
});
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
console.log("utilisateur serilizé!");
done(null, user.uid);
});
passport.deserializeUser(function(id, done) {
//console.log("coucou");
findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object.
// using https://gist.github.com/theangryangel/5060446
// as an example
passport.use(new FacebookStrategy({
clientID: 'XXX',
clientSecret: 'XXX',
callbackURL: "http://localhost:1337/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOne({uid: profile.id}, function(err, user) {
if (err) { return done(err); }
if (user) {
//console.log('momo');
User.update({uid : user.uid},{token : accessToken},function(){done(null, user);});
} else {
console.log(profile);
var user_data = {
token : accessToken
, provider: profile.provider
, alias: profile.username
, uid: profile.id
, created: new Date().getTime()
, name: {
first: profile.name.givenName
, last: profile.name.familyName
}
, alerts: {
email: true
, mobile: false
, features: true
}
};
console.log(user_data);
User.create(user_data).done(function(err, user) {
console.log(err);
if(err) { console.log("err");throw err; }
done(null, user);
});
}
});
}
));
While I do not have a direct answer for you, this was extremely useful to when getting it to work with GitHub OAuth: https://github.com/stefanbuck/sails-social-auth-example/blob/master/config/middleware.js
This is an entire, recent, Sails.js application implementing passport so it might be of use to you to side-by-side the two in the debugger and find out what is going on.
Check out this easy and full implementation for sails.js with passport.js supporting both Email, Twitter and Facebook.
https://github.com/bmustata/sails-auth-super-template

Resources