How to protect route endpoints using Passport? - javascript

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.

Related

How to do user login correctly with nodejs and express

I am very new to web development, and have been using Google as a guide.
If I put a wrong login that does not match what I have in my database, the website just gets stuck and keeps trying to “load”. I also am confused on how to do token-based authentication for login and would love some more guidance on that, the guide I am following talks about database encryption and OAuth 2.0 with Google.
If the user logs in with a username and password that is not correct, I just want it to give an error and reload back to login.ejs.
Thank you for any help!
The issue might be you are not returning anything when foundUser is null or if the password doesn’t match.
If there is any error you can redirect it to the /login route with query param (err) which can be read by the client using JS at page load. If there is a nonempty query param err then read it and show it in some popup.
res.redirect("/login?err=The username does not exist");
//connect to mongodb
mongoose.connect("mongodb://localhost:27017/userDB", {
useNewUrlParser: true
});
const userSchema = {
username: String,
password: String
};
const User = new mongoose.model("User", userSchema);
app.get("/", function(req, res) {
res.render("home"); //render home.ejs
});
app.get("/login", function(req, res) {
res.render("login"); //render login.ejs
});
app.post("/login", function(req, res) {
const username = req.body.username;
const password = req.body.password;
try {
User.findOne({
username: username
}, function(err, foundUser) {
if (err || !foundUser) {
return res.redirect("/login?err=The username does not exist");
}
if (foundUser.password !== password) {
// you can use bcryptjs for hashing and comparing hashed values.
return res.redirect("/login?err=The username or password is invalid");
}
res.redirect("/counter");
});
} catch (error) {
res.redirect("/login?err=something went wrong!");
}
});
You can read more about handling authentication in nodeJS here. also check passportjs

How to call deserializeUser with passport

I found the following post on how deserializeUser is supposed to work: PassportJS serializeUser and deserializeUser execution flow
However When I try and send a JSON to the server which contains the key for the user, I cannot call req.user to find the user's details.
I am unsure on how to check what is going on because a lot of stuff is going on under the hood.
Does passport expect that I send a cookie to express containing the key? Any specific name for the cookie or format?
Does it require that I sent the key in JSON format?
const express = require('express')
const app = express()
const mongoose = require('mongoose')
var cors = require('cors')
const bodyParser = require('body-parser')
var http = require('http')
var cookieParser = require('cookie-parser');
app.use(cookieParser('someSecret'));
var session = require('express-session');
app.use(session());
var flash=require("connect-flash");
app.use(flash());
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: true,
passReqToCallback: true
},
function(req, username, password, done){
registrationModel.findOne({username: username}, function(err, user){
if (err) { return done(err); }
if (!user){
console.log("Username incorrect")
return done(null, false, { message: 'Incorrect username.'});
}
if (user.password != password){
console.log("Password Incorrect")
return done(null, false, { message: 'Incorrect Password.'});
} else {
console.log("Returning good stuff")
console.log(user)
return done(null, user);
}
});
}
));
//Needed for authenticating the session and initializing passport
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
console.log("Serializing User")
console.log(user.id)
console.log("Should be serialized")
done(null, user.id);
});
passport.deserializeUser(function(id, done){
users.User.findById(id, function(err, User) {
console.log('attempting to deserialize user')
console.log(user)
console.log('--------------')
if (User){
done(err, User);
} else {
done(err, null);
}
})
})
app.post('/api/authenticate', passport.authenticate('local'),
function(req, res){
const individualIWant = jsonQuery('req.sessionStore.sessions')
res.cookie('FreeUp', req.session.passport.user)
var cookie = JSON.stringify(req.session.passport.user)
res.send({'name': 'FreeUp', 'value': cookie})
})
I can see a cookie in the browser:
name:"FreeUp"
value:""59c4cf4ecb364a000f23a707""
The value is the database ID for the object and not the key for the serialized object. I understand that I need to access the key for serialized object by using res.req.sessionID and to send that back instead.
However the point still stands, I do not know whether to send it back as a json file or a cookie. It seems like such a complex library to use.
Which format does passport expect me to use when sending the data back from Ember?
Thanks!
I had a lot of trouble with this as well.
From what I understand Passport doesn't actually create a session itself. So you need to use another piece of middleware, like express-session, and you'll also need a cookie parser.
I built my authentication using the tutorial they have at Scotch: https://scotch.io/tutorials/easy-node-authentication-setup-and-local. It's really well commented and the only straight forward tutorial I could find (especially for local authentication). I can verify that it worked for me and req.user is storing all the credentials when I pass it in my routes.
Hope that helps.

Destroying JSON Web Tokens

This is something,that really confuses. me. Let us suppose you have a REST API where you want the user to logout. After login out,the jwt(json web token) should be destroyed,so the user can not have access to the server's resources(ie menu,dishes etc).
In my case the user can logout,but he/she can still perform all the requests(get dishes,post and delete),until the token is valid. Here is my code.
verify.js
var User = require('../models/user');
var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var config = require('../config.js');
exports.getToken = function (user) {
return jwt.sign(user, config.secretKey, {
expiresIn: 3600
});
};
exports.verifyOrdinaryUser = function (req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, config.secretKey, function (err, decoded) {
if (err) {
var err = new Error('You are not authenticated!');
err.status = 401;
return next(err);
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
var err = new Error('No token provided!');
err.status = 403;
return next(err);
}
};
I am invalidating the token after a period of 1 hour.
And users.js where I set all the routes with their tasks. ie localhost:3000/users/login,localhost:3000/users/register and localhost:3000/users/logout. So.
var express = require('express');
var router = express.Router();
var passport = require('passport');
var User = require('../models/user');
var Verify = require('./verify');
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.post('/register', function(req, res) {
User.register(new User({ username : req.body.username }),
req.body.password, function(err, user) {
if (err) {
return res.status(500).json({err: err});
}
passport.authenticate('local')(req, res, function () {
return res.status(200).json({status: 'Registration Successful!'});
});
});
});
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.status(401).json(
err: info
});
}
req.logIn(user, function(err) {
if (err) {
return res.status(500).json({
err: 'Could not log in user'
});
}
var token = Verify.getToken(user);
res.status(200).json({
status: 'Login successful!',
success: true,
token: token
});
});
})(req,res,next);
});
router.get('/logout', function (req, res) {
req.logout();
res.status(200).json({
status: 'Bye!'
});
});
module.exports = router;
It seems that the logout method req.logout,doesn't work:(. Any ideas?
Thanks,
Theo.
You cannot log out a user that has a valid token if all the data is on the client side. You would need to store some state on the server to distinguish between users that you explicitly logged out and those that you didn't and check this state every time. If all of the data is entirely in the JWT token then you can't do anything to make it invalid (other than changing your secret that would invalidate all of the tokens, not just this one).
You actually discovered the main disadvantage of using authentication based entirely on the data that is included in the token itself. Those tokens cannot be invalidated. Once they're out then must be assumed to be active. You could only ask the client to forget it, but the client cannot be trusted to do that.
In theory you might have a fast data store like Redis where you keep all of the valid tokens and remove tokens from there to force logout, and check this storage on every request to know who is still logged in and who is not, but if you do that then you might store the session data in Redis in the first place and give only some random keys to that data store to the clients.
JWT is designed to be stateless. This means that all the information needed is contained in the token itself.
As the token has already been created, logout will have no effect on the validity of this.
This leaves you needing to keep a list of 'invalidated' tokens, which means you have once more introduced state.
If you are only concerned about subsequent users on the same machine, you could delete the token on logout, thus preserving the statelessness, but this will not protect against cases where the token has been captured.

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

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);
});

passportjs authentication using google apps email id

I am trying passport js with google app email id. I am able to authenticate using gmail.com email id. But how can I authenticate if the email id is a google app id (google.com/a/companyname.com).
This is my code
var express = require('express');
var app = express();
var passport = require('passport');
var GoogleStrategy = require('passport-google').Strategy;
passport.use(new GoogleStrategy({
returnURL: 'http://10.3.0.52:3000/auth/google/return',
realm: 'http://10.3.0.52:3000/'
},
function(identifier, profile, done) {
User.findOrCreate({
openId: identifier
}, function(err, user) {
done(err, user);
});
}
));
app.get('/auth/google', passport.authenticate('google'));
app.get('/auth/google/return',
passport.authenticate('google', {
successRedirect: '/',
failureRedirect: '/login'
}));
app.get('/', function(req, res){
res.writeHead(200);
res.end("connected");
});
app.listen(process.env.PORT || 3000);
Your code is missing some vital parts:
...
passport.use(...); // this you have
// these are required as well.
app.use(passport.initialize());
app.use(passport.session());
// please read docs for the following two calls
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
...
With those in place, I can log in using my Google App address just fine.
EDIT: it only works with Node 0.8 though, Node 0.10 gives an error. I think using passport-google-oauth is a better solution anyway. For that, you have to register your application with Google (here); after registration, you'll be supplied both the GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET codes which you can use.
I have created a method that verifies if the email domain is the one i want to authorize:
UserSchema.method('checkFordomain', function(value) {
var parts = value.split('#');
return (parts[1] == 'companyname.com');
});
this is method I put in the model of the user model, using mongoose schema models
if (!user.checkForMMdomain(profile.emails[0].value)) {
return done();
}
in the callback of the passport google strategy
https://github.com/jaredhanson/passport-google-oauth
In your passport.use callback you can perform additional checking based on the domain of the primary email address (or whatever you are checking):
if (profile.emails[0].split('#')[1] !== authorizedDomain) {
return done(null, false);
}

Categories