req.user is undefined when using PassportJS, SequelizeJS and JWT tokens - javascript

I already checked multiple answers here on Stackoverflow, and also went through on the documentation but I still cannot find out what could be the problem. In my application I'm using SequelizeJS to access to my mySQL database and now I'm trying to secure my REST API endpoints with PassportJS using the JWT Strategy.
./app.js
// ...
// passport
app.use(passport.initialize());
require('./config/passport')(passport);
// ...
./config/passport.js
var passport = require('passport');
var passportJwt = require('passport-jwt');
var models = require('../models');
var config = require('./config');
var ExtractJwt = passportJwt.ExtractJwt;
var Strategy = passportJwt.Strategy;
module.exports = function(passport) {
var params = {
secretOrKey: config.jwt.secret,
jwtFromRequest: ExtractJwt.fromAuthHeader()
};
passport.use(new Strategy(params, function(jwt_payload, done) {
models.User.findOne({
where: {
id: jwt_payload.id
}
}).then(
function(user) {
if (user) {
done(null, user);
} else {
done(null, false);
}
},
function(err) {
return done(err, false);
}
);
}));
};
I'm trying to get the user entity from the request of this simple route:
var router = express.Router();
// ...
router.route('/user/me', passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});
I already created another route which returns a JWT token based on the provided username and password. When I call the /user/me endpoint I attach the JWT token into the header, for example:
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.M9z3iWNdjAu4THyCYp3Oi3GOWfRJNCYNUcXOw1Gd1Mo
So, my problem is that when I call the /user/me endpoint with a token, the req.user will be undefined and I cannot figure it out what is the reason.
Thank you in advance for your help!

Your route definition seems to be wrong: router.route doesn't accept a middleware in its second argument, so authentication does not happen at all.
It should be smth like
var router = express.Router();
// ...
router.route('/user/me')
.all(passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});

Related

How to access Passport 'done' function from rest of the code while authenticating using passport.js?

I am trying to implement authentication in my API using passport.js with passport_jwt strategy. the code is attached below
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const opts = {}
const User = require('../models/user_model')
const dotenv = require('dotenv')
module.exports = function(passport) {
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = process.env.JWT_SECRET;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
here I have passed the user on the done function on successful authentication. For my post routes I used passport.authenticate as a route middleware like below.
app.use('/api/v1/posts', passport.authenticate('jwt', { session : false }), postRoutes)
Now the question is how can I access the user, previously sent on the done function, while creating the post routes? Thank you so much.
You can access user in your postRoutes method as code below:
exports.postRoutes = async (req, res) => {
console.log('req.user =====>', req.user);
res.status(200).send({ user: req.user })
};

TokenError: Bad Request; Google OAuth2; Passport.js on Node.js; Able to console.log data, however delivers error

I am attempting to use Passport.js to authorize Google OAuth2 on Node.js. I have tried all week to make it work and have no idea why it isn't, so am now resorting to stack for some potential help. I have tried all solutions to similar problems available on forums online.
Each time it sends the request it returns TokenError: Bad Request, however, it is able to console.log the required data, so this to me demonstrates that the token was in fact successful. I cannot explain why this is occurring.
I have tried being more specific in callback request e.g http://localhost:3000/auth/google/redirect.
I have tried every other type of Oauth type google has Node server, web application, html ect.
I have tried different ports.
AUTH ROUTES
const router = require('express').Router();
const passport = require('passport');
// auth login
router.get('/login', (req, res) => {
res.render('login', { user: req.user });
});
// auth logout
router.get('/logout', (req, res) => {
// handle with passport
res.send('logging out');
});
// auth with google+
router.get('/google', passport.authenticate('google', {
scope: ['profile']
}));
// callback route for google to redirect to
// hand control to passport to use code to grab profile info
router.get('/google/redirect', passport.authenticate('google'),
(req,
res) => {
res.send('you reached the redirect URI');
});
module.exports = router;
PASSPORT_SETUP
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const keys = require('./keys');
passport.use(
new GoogleStrategy({
// options for google strategy
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret,
callbackURL: '/auth/google/redirect'
}, (accessToken, refreshToken, profile, done) => {
// passport callback function
console.log('passport callback function fired:');
console.log(profile);
})
);
When submitted the process progresses through SignIn page, delivers desired result the console.log and then just sits for about 1 minute awaiting localhost.
As you can see the very thing it is trying to retrieve is already in the console.
It then progresses to throw and Error:
Sorry for the late reply, dug up some old code this is the point where it was marked as 'All auth methods functioning'. As stated by Aritra Chakraborty in the comments, "done" method was not being called. See the following implementation with Nedb.
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const Datastore = require('nedb');
const database = new Datastore('database.db');
database.loadDatabase();
passport.serializeUser((user, done) => {
done(null, user.googleId || user.id);
});
passport.deserializeUser((googleId, done) => {
database.findOne({ googleId : googleId }, (err, user) => {
done(null, user);
});
});
var strategy = new GoogleStrategy({
// options for google strategy
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret,
callbackURL: '/auth/google/redirect'
}, (accessToken, refreshToken, object0, profile, done) => {
// check if user already exists in our own db
database.findOne({ googleId: profile.id }, (err, currentUser) => {
if (currentUser !== null) {
done(null, currentUser);
} else {
var d = new Date();
var n = d.getTime();
var duoID = uuidv1();
var User = {
duoVocalID: duoID,
googleId: profile.id,
username: profile.displayName,
thumbnail: profile._json.image.url,
oscope: object0.scope,
oaccess_token: object0.access_token,
otoken_type: object0.token_type,
oid_token: object0.id_token,
oexpires_in: object0.expires_in,
oemails: profile.emails,
olanguage: profile._json.language,
oname: profile.name,
TimeOfLastLogon: n,
RefreshToken: refreshToken
};
database.insert(User, (err, newUser) => { });
var newUser = User;
done(null, newUser);
}
});
});
passport.use(strategy);
// auth with google+
app.get('/auth/google', passport.authenticate('google', {
scope: ['profile', 'email', 'https://www.googleapis.com/auth/spreadsheets'],
accessType: 'offline',
approvalPrompt: 'force'
}));
// callback route for google to redirect to
// hand control to passport to use code to grab profile info
app.get('/auth/google/redirect', passport.authenticate('google'), async (req, res) => {
var userString = JSON.stringify(req.user)
jwt.sign({userString}, 'secretKey', { expiresIn: '365d' }, (err, token) => {
res.send("<script>localStorage.setItem('token', '"+token+"'); window.close(); window.opener.document.getElementById('modal-toggle').checked = false;</script>");
});
});

How to get express route to go through passport for authentification

I'm recently changing a boilerplate I made in es6 to a slightly older version es5. I had to recreate the export, require instead of using imports and the routing is functional now.
I have a structure like this:
index.js (app)
api/index.js (/api endpoint)
api/auth.js (/api/auth endpoint)
api/protected.js (/api/protected endpoint)
In my auth route I have the login and register functionning and generating a token:
const token = jwt.sign({id: req.body.username}, config.jwtSecret)
In my protected.js I have the following:
const express = require('express');
const router = express.Router();
const passport = require('../config/passport');
router.use(passport.initialize({ session: false }));
router.use(passport.session());
router.get('/test', passport.authenticate('jwt') , (req, res) => {
res.status(200).json({ message: 'Hello sweetie', auth: req.isAuthenticated(), user: req.session.passport.user})
});
module.exports = router;
And in my passport.js
const
passport = require('passport'),
config = require('./../config'),
passportJwt = require('passport-jwt'),
JwtStrategy = passportJwt.Strategy,
ExtractJwt = passportJwt.ExtractJwt,
userSchema = require('./../schemas/userSchema');
passport.serializeUser(function(user, done) {
console.log(JSON.stringify(user));
done(null, user[0].username);
});
passport.deserializeUser(function(username, done) {
console.log('DESER -- '+username);
userSchema.checkUsername(username)
.then(user => {
console.log(user[0]);
done(null, user[0]);
})
.catch(err =>{
console.log(JSON.stringify(err));
});
});
const jwtOptions = {
secretOrKey: config.jwtSecret,
jwtFromRequest: ExtractJwt.fromHeader('authorization'),
}
passport.use('jwt', new JwtStrategy(jwtOptions, function(jwt_payload, done) {
console.log('Strategy: '+ jwt_payload.id);
userSchema.checkUsername(jwt_payload.id)
.then(user => {
console.log(user[0].username);
if(user[0]) return done(null, user)
else return done(null, false)
})
.catch(err =>{
console.log(JSON.stringify(err));
});
}));
module.exports = passport;
The problem is when I try to access: 127.0.0.1:8080/api/protected/test
with the token I got on login it give me a: Unauthorized
Furthermore the console.log inside the:
serializeUser
deserializeUser
jwt Strategy
Are never shown and thus I think the passport middleware isn't used.
How could I get the /protected routes to use the passport middleware?
Edit: I have tried printing the passport object and I can see my jwt strategy is indeed defined. So I don't see why it doesn't want to go through it.
The problem is that your passport is looking for authorization header not Authorization header.
Change your
jwtFromRequest: ExtractJwt.fromHeader('authorization')
to
ExtractJwt.fromAuthHeaderAsBearerToken()
so your options will look like
const jwtOptions = {
secretOrKey: config.jwtSecret,
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
}
Now what this does is passport will look for Authorization header with value bearer your_jwt_token.
https://www.npmjs.com/package/passport-jwt

How to protect route endpoints using Passport?

I am trying to build user authentication into my simple Node.js app using the tutorial here: http://code.tutsplus.com/tutorials/authenticating-nodejs-applications-with-passport--cms-21619
It works great in terms of protecting the application home page so that it can only be accessed after logging in, but I am having a really hard time restricting my REST endpoints to only logged in users. As in using Postman I can still call the end points without any authentication.
In my route I have the following:
var express = require('express');
var router = express.Router();
// if the user is authenticated
var isAuthenticated = function (req, res, next) {
if (req.isAuthenticated())
return next();
res.json("not authenticated");
}
/*
* GET carlist.
*/
router.get('/carlist', isAuthenticated, function(req, res) {
var db = req.db;
var collection = db.get('carlist');
collection.find({},{},function(e,docs){
res.json(docs);
});
});
This doesn't seem to work, even if I actually enter correct credentials I am always returned "not authenticated". What I am I missing here?
EDIT:
Full code here: https://gist.github.com/tudorific/d99bc51cfbd3d9d732a3bb1b93ed7214
Thanks in advance for the help!
I figured it out. Since I was using a LocalStrategy the IsAuthenticated method was looking for the credentials in the session rather than at the Basic Credentials I was sending with Postman. So I needed to create the following new BasicStrategy:
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var Employer = require('../models/employer');
var bCrypt = require('bcrypt-nodejs');
passport.use(new BasicStrategy(
function(username, password, done) {
Employer.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
//if (!user.validPassword(password)) { return done(null, false); }
if (!isValidPassword(user, password)){ return done(null, false); }
return done(null, user);
});
var isValidPassword = function(employer, password){
return bCrypt.compareSync(password, employer.password);
}
}));
And then use that strategy in my route like this:
router.get('/carlist', passport.authenticate('basic', function(req, res) {
var db = req.db;
var collection = db.get('cars');
collection.find({},{},function(e,docs){
res.json(docs);
});
});
This would use my basic authentication credentials from Postman to connect to the website.
Thanks to Neta Meta's advice in the comments to my OP I was able to arrive to this result and a bit more reading on the Passport documentation to understand the differences between the strategies.

How can I manage sessions with Passport JS for some routes using express?

I got below express node.js server code using Passport. At it my whole routes definition depends upon a MongoDB connection using mongo-db but model used by Passport is done through another connection by mongoose. I mention these two details cause I think it should also be coded in a better way.
However, the main problem is that even though Passport it's doing it's work, I still can go to localhost/registro directly no matter I didn't logged in first.
When someone tried to access to localhost/registro it should be redirected to start page if a login and authentication wasn't done first.
I care about a safe implementation of it, I'd also like to have some information about the user during the session time.
I'm quite confused about what I should try, cookies, sessions, etc. Apart that in new express version middlewares work different than before.
This is my server.js:
var express = require('express')
var mongodb = require('mongodb')
var mongoose = require('mongoose')
var bodyParser = require('body-parser')
var passport = require('passport')
var LocalStrategy = require('passport-local').Strategy;
var app = express()
var BSON = mongodb.BSONPure
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(__dirname+"/public"))
app.use(bodyParser())
var MongoDBClient = mongodb.MongoClient
mongoose.connect('mongodb://localhost/psicologosTuxtepecDB')
var Schema = mongoose.Schema
var userCredential = new Schema({
username: String,
password: String
}, {
collection: 'members'
})
var userCredentials = mongoose.model('members', userCredential)
passport.serializeUser(function(user, done) {
done(null, user);
})
passport.deserializeUser(function(user, done) {
done(null, user);
})
passport.use(new LocalStrategy(function(username, password, done) {
process.nextTick(function() {
userCredentials.findOne({
'username': username,
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != password) {
return done(null, false);
}
return done(null, user);
});
});
}));
MongoDBClient.connect("mongodb://localhost/psicologosTuxtepecDB", function (error, psicologosTuxtepecDB) {
if (error) {
console.log("We've got a connection error, so far we should take this function better for a correct debug")
}
else {
console.log("Connection to psicologosTuxtepecDB has been successful")
// Seleccionamos una colección
var psicologosCollection = psicologosTuxtepecDB.collection("psicologos")
app.get('/registro', function(request,response) {
response.sendfile("public/html/registro.html")
})
// Cuando nos hagan una petición HTTP de tipo POST en la ruta psicologos...
app.post("/psychos", function(request, response) {
var psychologist = {
personalData: request.body._personalData,
professionalData: request.body._professionalData,
professionalInterests: request.body._professionalInterests
}
psicologosCollection.insert(psychologist, function(error, responseFromDB) {
if (error) {response.send(responseFromDB)}
console.log("Se ha insertado: "+ JSON.strinfigy(responseFromDB))
response.send(responseFromDB)
})
})
app.get("/psychos/:id", function(request, response) {
var id = new BSON.ObjectID(peticion.params.id)
psicologosCollection.findOne(
{'_id':id},
function(error,responseFromDB) { if (error) {response.send(responseFromDB)} response.send(responseFromDB)}
)
})
app.get("/psychos", function(request,response) {
psicologosCollection.find().toArray(function(error,responseFromDB) {
if (error) {response.send(responseFromDB)}
response.send(responseFromDB)
})
})
app.post('/login',
passport.authenticate('local', {
successRedirect: '/loginSuccess',
failureRedirect: '/loginFailure'
})
)
app.get('/loginFailure', function(req, res, next) {
res.redirect('/')
})
app.get('registro', function(request, response) {
response.sendfile('public/html/registro.html')
})
app.get('/loginSuccess', function(req, res, next) {
res.redirect('/registro')
})
app.listen(80, function () {
console.log("app escuchando en el puerto Maricela fecha de nacimiento DDMM")
})
}
})
These are my Passport statements:
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
done(null, user);
})
passport.deserializeUser(function(user, done) {
done(null, user);
})
passport.use(new LocalStrategy(function(username, password, done) {
process.nextTick(function() {
userCredentials.findOne({
'username': username,
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (user.password != password) {
return done(null, false);
}
return done(null, user);
});
});
}));
Express "chains" route methods. The basic idea behind securing routes in Express.js is to have a method that checks the a&a before allowing the request to proceed to the intended route. There are a few ways to do this:
Method 1: Add the auth method to the route declaration
function requireAuth(req,res,next){
if user is authenticated
next();
else
res.send(401);
}
app.get('/registro', requireAuth, function(request, response) {
response.sendfile('public/html/registro.html')
})
Method 2: Declare a route handler for your auth
app.get('/registro', function(req,res,next){
if user is authenticated
next();
else
res.send(401);
})
app.get('/registro', function(request, response) {
response.sendfile('public/html/registro.html')
})
Method 3: Use app.use() instead of a verb
With this method, you need to consider when app.router gets inserted into the middle-ware.
Edit 1: Where would the require auth method be declared
If you plan on placing route handlers in multiple .js files, it's a good idea to put your require auth mehtod in a separate .js file as well and require it where appropriate.
Otherwise, you can just stick it in the same file with everything else.
Edit 2: How do sessions work in Express.js and Passport.js
From the Passport.js documentation, you first need to configure the express session before the passport session:
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
Note: You should probably consider using something other than the memory store for session management.
Along with the serializeUser and deserializeUser methods, at this point Passport will have placed a .user on the request.
You can also use req.isAuthenticated() to determine if the user is authenticated.
Note 2: I've had problems getting the serializeUser and deserializeUser methods to work with Passport-Saml. If that is the case, just manage the session yourself.

Categories