Passport js, verify accessToken with PING - node.js

I am creating an AWS Lambda with API Gateway (doesn't really matter where, just an API) that needs to authenticate the accessToken passed to it against a PING Federate instance.
I'm using Node.js and Passport.js, I have everything set, but doesn't seem to be calling the function. I'm not using middleware like express.
I'm setting these constants
const passport = require('passport');
const OAuth2Strategy = require('passport-oauth2');
Then in the handler, I have this (I know it's not correct, but I've tried a number of things and can't quite get it right.
passport.use(new OAuth2Strategy({
authorizationURL: 'http://localhost',
tokenURL: 'http://localhost',
clientID:'myID',
clientSecret: 'mySecret',
callbackURL: 'http://localhost'
},
function(accessToken, refreshToken, profile, done) {
console.log(console.log(`{"level": "INFO", "oAuth Info": "Finding one"}`));
User.findOne({ 'ping.id' : profile.id }, function (err, user) {
if (err) {
console.log(console.log(`{"level": "INFO", "oAuth Error": "${err}"}`));
return done(err);
}
if (user) {
console.log(console.log(`{"level": "INFO", "oAuth Success": "${user}"}`));
return done(null, user);
} else {
console.log(console.log(`{"level": "INFO", "oAuth Else": "Why am I here"}`));
var newUser = new User();
newUser.ping.id = profile.id;
newUser.ping.token = accessToken;
newUser.ping.name = profile.displayName;
newUser.ping.email = profile.email;
newUser.save(function(err) {
if (err) { throw err; }
return done(null, newUser);
});
}
});
}
));
Then I've called it in the handler like this.
let token = event.headers.authorizationToken;
passport.authenticate('ping-oauth2', function(token) {
console.log(console.log(`{"level": "INFO", "authenticate callback": "am i back"}`));
if (err) { return err.message; }
});
What am I doing wrong? Thanks.

Related

REST API is not changing users according to Passport JWT Token in header?

So I have a REST API written in Node.JS, MongoDB, and Express. I'm using passport and passport-jwt to authenticate using a JSON web token, but when I use different tokens for different user accounts, the request is saving the same user everytime.
Here is my authenticate endpoint in routes.js:
// Authenticate the user and get a JSON Web Token to include in the header of future requests.
apiRoutes.post('/authenticate', function(req, res) {
User.findOne({
email: req.body.email
}, function(err, user) {
if (err) throw err;
if (!user) {
res.send({ success: false, message: 'Authentication failed. User not found.' });
} else {
// Check if password matches
user.comparePassword(req.body.password, function(err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user, config.secret, {
//expiresIn: 10080 // in seconds
});
res.json({ success: true, token: 'JWT ' + token, _id: user._id });
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
}
});
}
});
});
So, if the user credentials match in the database, it creates a new token and returns it. Then I put that token into the header of this endpoint:
// GET user
apiRoutes.get('/users', passport.authenticate('jwt', { session: false }), function(req, res) {
res.json(req.user);
});
I could authenticate a new user every time and get a different token every time, but the /users endpoint always returns the same user. Everything else is working in the API, the database is getting updated when registering new users and other things are being returned, but even with different tokens the same user is returned from passport. I know that I am putting the header in the right way in Postman with the key of Authorization and the value of the token with JWT in front of it, but I don't understand why this is happening.
Here is my passport.js if it matters:
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/user');
var config = require('./main');
// Setup work and export for the JWT passport strategy
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Any ideas?
Mongo uses a "_" before the document id. Instead of having User.findOne({id: jwt_payload.id} you should have User.findOne({_id: jwt_payload._id} and that should fix the issue.
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/user');
var config = require('./main');
// Setup work and export for the JWT passport strategy
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({_id: jwt_payload._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
So I figured out that it was displaying the first entry in the user document because the JWT was too big. So I changed the authenticate route to make the JWT out of just the user's id and username like so:
apiRoutes.post('/authenticate', function(req, res) {
User.findOne({
email: req.body.email
}, function(err, user) {
if (err) throw err;
if (!user) {
res.send({ success: false, response: 'Authentication failed. User not found.' });
} else {
// Check if password matches
user.comparePassword(req.body.password, function(err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign({id: user._id, username: user.username }, config.secret, {
expiresIn: 10080 // in seconds
});
res.json({ success: true, response: 'Authentication succeeded! User found.', token: 'JWT ' + token});
} else {
res.send({ success: false, response: 'Authentication failed. Passwords did not match.' });
}
});
}
});
});
Payload has lot more data after jwt.sign in version 5.5.4, now _id is a part of _doc under payload.
As "SamuelDuke" suggest above is correct, just change to jwt_payload._doc._id.
See below updated code.
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/user');
var config = require('./main');
// Setup work and export for the JWT passport strategy
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({_id: jwt_payload._doc._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};

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!

From facebook token to jwt token

I'm using the passport module to authenticate a user and generate a jwt token.
Now I want to make facebook login possible. I'm in the phase that I get a fb token and facebook id.
What am I supposed to do with this info? Currently I make a new user that has a variable with facebook id but without a password.
Without a password I can't generate a jwt token, thus I cannot login.
How does this work?
My front end is angular and my API is written with nodejs, express & mongoose.
Passport code:
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
generatejwt code:
UserSchema.methods.generateJWT = function() {
// set expiration to 60 days
var today = new Date();
var exp = new Date(today);
exp.setDate(today.getDate() + 60);
return jwt.sign({
_id: this._id,
username: this.username,
exp: parseInt(exp.getTime() / 1000),
}, 'SECRET');
};
You need to create 2 routes :
1) This will be called to login to facebook.
2)Callback route if the user authentication is successful.(This callback route needs to be registered in the facebook developer portal.
//Facebook Login
app.route('/auth/facebook')
.get(passport.authenticate('facebook', { scope: 'email' }));
// Callback
app.route('/auth/facebook/callback')
.get(passport.authenticate('facebook'),users.generateJWT);
Define a facebook strategy that will store the user details (id, token, email) in your database
var facebookStrategy = new FacebookStrategy({
clientID: cfg.facebook.clientID,
clientSecret: cfg.facebook.clientSecret,
callbackURL: cfg.facebook.callbackURL,
profileFields: ['id', 'email', 'first_name', 'last_name']
},
function(token, refreshToken, profile, done) {
console.log("Token: "+ token);
console.log("RefreshToken: "+ refreshToken);
console.log("profile: "+ profile);
process.nextTick(function() {
User.findOne({ 'facebook.id': profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
console.log("User Found");
return done(null, user);
} else {
console.log("Creating user");
var newUser = new User();
console.log("Token: "+ token);
newUser.facebook.id = profile.id;
newUser.facebook.token = token;
newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
newUser.facebook.email = (profile.emails[0].value || '').toLowerCase();
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
});
In the controller, write the below code for callback route:
/**
* Generate JWT Token
*/
exports.generateJWT = function(req, res) {
var token;
token = req.user.generateJwt();
res.status(200);
res.json({
"token" : token
});
};

Nodejs express integration authentification local and facebook

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

Passport-facebook access req object from within callback function

On the callback from Facebook for nodejs passport authentication, how do you get the req object within the callback?
passport.use(new FacebookStrategy({
clientID: 123456789,
clientSecret: 'SECRET',
callbackURL: "http://example.com/login/facebook/callback"
},
function(accessToken, refreshToken, profile, done){
// Is there any way to get the req object in here?
}
));
Setting the passReqToCallback option, as so:
passport.use(new LocalStrategy({ passReqToCallback: true },
function(req, username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) {
req.flash('error', 'Your password is too long');
req.flash('error', 'Also, it is too short!!!');
return done(null, false);
}
return done(null, user);
});
}
));
req becomes the first argument to the verify callback
As per https://github.com/jaredhanson/passport/issues/39
I am answering it too late, but i think my solution is better and more conventional.
In the official documentation here. There is a section "Association in Verify Callback", in which it is mentioned that if we set the strategy's passReqToCallback option to true, this enables req and it will be passed as the first argument to the verify callback.
So my FacebookStrategy now looks like:
var User = require('../models/UserModel.js');
var FacebookStrategy = require('passport-facebook').Strategy;
exports.facebookStrategy = new FacebookStrategy({
clientID: 'REPLACE_IT_WITH_CLIENT_ID',
clientSecret: 'REPLACE_IT_WITH_CLIENT_SECRET',
callbackURL: 'http://localhost:3000/auth/facebook/callback',
passReqToCallback: true
},function(req,accessToken,refreshToken,profile,done){
User.findOne({
'facebook.id' : profile.id
},function(err,user){
if(err){
done(err);
}
if(user){
req.login(user,function(err){
if(err){
return next(err);
}
return done(null,user);
});
}else{
var newUser = new User();
newUser.facebook.id = profile.id;
newUser.facebook.name = profile.displayName;
newUser.facebook.token = profile.token;
newUser.save(function(err){
if(err){
throw(err);
}
req.login(newUser,function(err){
if(err){
return next(err);
}
return done(null,newUser);
});
});
}
});
}
);
In my code sample i have added some logic to save user info in DB and saving user details in session. I thought it might be helpful to people.
req.user gives the information of user stored in passport session.

Resources