Passport that authenticating user - node.js

I have the following code
passport.authenticate("local", (err, token, user) => {
if (err) {
console.dir('test4');
console.dir(err);
return res.json({
status: "error",
error: "Invalid user.",
});
}
console.dir(token);
if (!user) {
console.dir('test5');
return res.json({
status: "error",
error: "Invalid user.",
});
} else {
req.logIn(user, (err) => {
if (err) {
console.dir('test6');
console.dir(err);
return res.json({
status: "error",
error: "Invalid user.",
});
}
res.clearCookie("auth");
res.cookie("auth", token);
req.session.user = result;
return res.json({
status: "ok",
result: {
token,
user
}
});
});
}
})(req, res, next);
})
.catch((err) => {
console.dir(err);
return res.json({
status: "error",
error: _.isObject(err) ? "There was an error processing your request. Please try again later." : err,
});
});
I'm not able to authenticate the user using passport. Here's my passportConfig
const User = require("./models/user");
const LocalStrategy = require("passport-local").Strategy;
const jwt = require('jsonwebtoken');
module.exports = function (passport) {
passport.use(
new LocalStrategy((username, password, done) => {
User.login(username, password).then(result => {
const payload = {
sub: result[0].id
};
// create a token string
const token = jwt.sign(payload, process.env.JWT_SECRET);
return done(null, token, result[0]);
}).catch(err => {
return done(null, false);
});
})
);
passport.serializeUser((user, cb) => {
cb(null, user.id);
});
passport.deserializeUser((id, cb) => {
console.dir('test33');
console.dir(id);
User.fetchByEmail(id).then(result => {
cb(null, result[0]);
}).catch(err => {
cb(err, null);
});
});
};
and here's my index.js
app.use (
session ({
secret: "x",
resave: false,
saveUninitialized: true,
// proxy: true,
// rolling: true,
cookie: {
expires: new Date(Date.now() + (60*60*1000*24*90)),
secure: true
}
})
);
app.use(cookieParser());
app.use(passport.initialize());
app.use(passport.session());
require("./passportConfig")(passport);
what I'm I doing wrong?
The code brings out "test5".

Related

Cookies not being set when login using Google Login and passport

I am using nodejs and react on heroku and passport js. I have code that logs a user in using google; however, a cookie is not being set for that user. The first time the user tries to log in using google, he is redirected back to the server endpoint and he get's an error "Unable to verify authorization request state".
Here's my code
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_OAUTH_CLIENT_ID_LOGIN,
clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET_LOGIN,
callbackURL: process.env.DOMAIN_NAME + "/user/google/callback"
},
function(issuer, profile, cb) {
User.findOrCreateGoogleUser(profile.id, profile.emails[0].value).then(result => {
return cb(null, result[0]);
}).catch (err => {
console.dir(err);
return cb(err, null, {
message: err
})
});
}
));
passport.serializeUser((user, cb) => {
cb(null, user.id);
});
passport.deserializeUser((id, cb) => {
User.fetchById(id).then(result => {
cb(null, result[0]);
}).catch(err => {
cb(err, null);
});
});
router.get("/google/callback", (req, res, next) => {
passport.authenticate("google", {session: false}, (err, user, info) => {
if (err || !user) {
console.dir(err);
var message =
info && info.message
? encodeURIComponent(info.message + " Please try again or try another login method.")
: encodeURIComponent(
"There was an error logging you in. Please try another login method."
);
return res.redirect(
process.env.BASE_CLIENT_URL + "/login?error=" + message
);
}
req.login(user, {session: false}, (err) => {
if (err) {
console.dir(err);
return res.redirect(
process.env.BASE_CLIENT_URL +
"/login?error=" +
encodeURIComponent(
"Invalid User. Please try another account or register a new account."
)
);
}
const payload = {
sub: user.id,
};
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRESIN,
});
res.clearCookie("auth");
res.cookie("auth", token);
res.redirect(process.env.BASE_CLIENT_URL + "/loginsuccess");
});
})(req, res, next);
});
router.get(
"/google",
passport.authenticate("google", {
scope: ["email"],
})
);
const session = require("express-session");
app.use (
session ({
secret: "FMfcgzGllVtHlrXDrwtpNdhLRXlNtVzl18088dda1",
resave: false,
saveUninitialized: true,
proxy: true,
rolling: true,
cookie: {
expires: 60 * 60 * 24,
secure: (app.get('env') === 'production'),
sameSite: 'lax'
}
})
);
What am I doing wrong?

passport deserializeuser not being called

When I make a fetch request from my react frontend to login using passport.authenticate('./local), my passport.serializeUser is called but passport.deserializeUser is NOT (and req.user is not set).
I've read as many answers to this question on stackoverflow but to no avail. Here is my code below.
All this comes before my routes on the server.js:
//Express body parser
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.json());
//Express Session
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
cookie: {
secure: false
}
}));
// Passport config
require('./config/passport')(passport);
//Passport Middleware
app.use(passport.initialize());
app.use(passport.session());
Here is the passport config file:
module.exports = function(passport) {
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, async function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
const match = await bcrypt.compare(password, user.password);
if (!match) { return done(null, false); }
return done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
console.log('this gets called logged)
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log('this does NOT GET LOGGED');
User.findById(id, function(err, user) {
done(err, user);
});
});
};
Here is the react fetch request for the login route:
fetch('http://localhost:5000/authentication/login', {
method: 'POST',
body: JSON.stringify(this.state.formData),
headers: {"Content-Type": "application/json"},
mode: 'cors'
})
.then(res => res.json())
.then(resObject => {
if (resObject.errors) {
console.log('errors')
} else {
this.props.dispatch(handleLoginuser(resObject.user))
this.setState({
redirect: true
})
}
});
Here is the fetch request for another random route that should be protected:
componentDidMount() {
fetch('http://localhost:5000/protectedroute', {
method: 'GET',
mode: 'cors',
credentials: 'include'
})
.then(res => res.json())
.then(resObject => {
if(resObject.loggedIn) {
this.setState({
loggedIn: true
})
}
});
}
Here are the login and protected routes:
app.post('/authentication/login', passport.authenticate('local'), (req, res) => {
res.json({errors: false, user: req.user})
});
app.route('/protected')
.get(function(req, res) {
//req.user will be undefined!
if (req.user) {
return res.json({loggedIn: true})
} else {
return res.json({loggedIn: false})
}
})
I believe it's because of your post/login route on the back end.
Calling the passport.authenticate middleware does not complete the login process unless you use the boilerplate code from the official docs (which does not work with React)
You need to call req.login to complete the login process. Check out this example
app.post('/authentication/login', (req, res, next) => {
passport.authenticate('local', (err, theUser, failureDetails) => {
if (err) {
res.status(500).json({ message: 'Something went wrong authenticating user' });
return;
}
if (!theUser) {
res.status(401).json(failureDetails);
return;
}
// save user in session
req.login(theUser, (err) => {
if (err) {
res.status(500).json({ message: 'Session save went bad.' });
return;
}
console.log('---123456789098765432345678---', req.user);
res.status(200).json({{errors: false, user: theUser}});
});
})(req, res, next);
});
Setting up withCredentials: true while sending the post request worked for me.
axios.post(uri, {
email: email,
password: password
}, {
withCredentials: true
})

JSON Web TOken Strategy

This is passport function to extract headers. I am using fromAuthHeaderWithScheme one, I already tried fromAuthHeaderAsBearerToken with bearer token as well. I could not make it work no matter what?
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
User = require('../models/user'),
Config = require('../config/database');
module.exports = function(passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme("JWT");
opts.secretOrKey = Config.secret;
//Code only comes until here.
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
console.log(jwt_payload);//Code never reaches here.
User.getByUserId({
id: jwt_payload._id
}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
Next is my getUserById function
module.exports.getByUserId = function(id, cb) {
User.findById(id, cb)
}
Next, is where above two gets called:
router.post('/login', function(req, res) {
let username = req.body.username;
password = req.body.password;
User.getByUserName(username, function(err, user) {
if (err) {
throw err;
}
if (!user) {
return res.json({
success: "false",
msg: "User not found"
})
}
//if found compareUser to regiestred one
User.comparePassword(password, user.password, function(err, isMatched) {
if (err) {
throw err;
}
if (isMatched) {
const token = jwt.sign(user.toJSON(), CONFIG.secret, {
expiresIn: 3600 /*Logout in 1 hour*/
});
res.json({
success: "true",
token: 'JWT ' + token,
user: user._id,
email: user.email,
username: user.username,
});
} else {
return res.json({
success: "false",
msg: " Password not Matched"
});
}
});
});
});
And these are comparePassword and getUserByName incase you need to see:
module.exports.comparePassword = function(typedPassword, hash, cb) {
bcrypt.compare(typedPassword, hash, (err, isMatched) => {
if (err) {
throw err;
}
return cb(null, isMatched);
})
};
module.exports.getByUserName = function(username, cb) {
const query = {
username: username
}
User.findOne(query, cb);
}
The secret key is same every where, that is not the issue. I cannot seem to figure out the issue.
router.get("/profile", passport.authenticate('jwt', {
session: false
}, function(req, res, next) {
res.json({
success: true,
message: "This is user profile",
user: req.user
});
}));
Now, above is how I authenticate, using postman and sending request as content type "Authorization" and The token. Encase, any of you are wondering, I already tried 'bearer '+token through bearer scheme.
I changed the first code block I posted above to this
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
keys = require('./keys'),
mongoose = require('mongoose'),
User = require('../models/User');
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken('Bearer');
opts.secretOrKey = keys.secretOrKey;
module.exports = passport => {
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.sub }, (err, user) => {
User.findById(jwt_payload.id)
.then(user => {
if (user) {
return done(null, user);
}
return done(null, false);
})
.catch(err => console.log(err));
});
}))
};
And second block to this. Basically change the token from 'JWT' to 'Bearer'.
router.post('/login', (req, res) => {
const email = req.body.email, password = req.body.password;
User.findOne({ email: email })
.then(user => {
if (!user) {
res.status(404).json({ msg: 'User not found' })
}
//Check password
bcrypt.compare(password, user.password)
.then(isMatch => {
if (isMatch) {
//User found
//Create Jwt Payload
const payload = {
id: user.id,
name: user.name,
avatar: user.avatar
}
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 },
(err, token) => {
res.json({
success: true,
token: 'Bearer ' + token
});
});
} else {
return res.status(400).json({ password: 'Password do not match' })
}
})
// .catch(err => console.log(err));
})
});
Now its working for me.

passport JWT 'Unauthorized' Node

I'm making authorizing with token and when I test with postman, I keep getting 'Unauthorized' after logged in and go to profile
Login code:
router.post('/authentication', (request, response, next) => {
const email = request.body.email;
const password = request.body.password;
userModel.getUserByEmail(email, (err, user) => {
if (err) throw err;
else {
if (!user) {
console.log('User does not exists!');
response.json({ success: false, msg: 'User does not exists!' });
} else {
userModel.comparePassword(password, user.password, (err, isMatch) => {
if (err) throw err;
else {
if (!isMatch) {
response.json({ success: false, msg: 'Password do not match!' });
} else {
const token = jwt.sign(user, config.secret, {
expiresIn: 86400 // 1 day
});
response.json({
success: true,
token: "JWT " + token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
}
}
});
}
}
});
});
passport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const userModel = require('../models/usersModel');
const config = require('../config/database');
module.exports = function (passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
userModel.getUserById(jwt_payload._doc._id, (err, user) => {
console.log(jwt_payload);
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
tested route :
router.get('/profile', passport.authenticate('jwt', { session: false }), (request, response, next) => {
response.json({ user: request.user });
});
(PS: I tried console.log(jwt_payload); and shows nothing in console. Still stucking in this please help. I'm a starter. )
After you get your jwt token, you should send it in every request.
header: authorization: JWT "token"
Are you doing it?

Passport doesn't save the session

I have read a lot about this issue but any answer doesn't work for me. I am working with React, Express and Passport to manage my authenticate routine. The authentication routine is fine, and it makes the redirect that I want. But, when I refresh any route, it says me that I am not authenticate. It seems that Passport doesn't save the session. Here my code:
Server.js
const lisaApp = express();
lisaApp.use(bodyParser.json())
lisaApp.use(bodyParser.urlencoded({ extended: false }))
lisaApp.use(cookieParser())
lisaApp.use(session({
secret: config.secret,
resave: false,
saveUnitialized: false
}))
lisaApp.use(passport.initialize())
lisaApp.use(passport.session())
passport.use(auth.localStrategy);
passport.serializeUser(auth.serializeUser);
passport.deserializeUser(auth.deserializeUser);
lisaApp.post('/login', (req, res, next) => {
const validationResult = validateLoginForm(req.body);
if (!validationResult.success) {
return res.status(400).json({
success: false,
message: validationResult.message,
errors: validationResult.errors
});
}
return passport.authenticate('local', (err, userData) => {
if (err) {
if (err.response.statusText === 'Unauthorized') {
return res.status(400).json({
success: false,
message: 'The password is not right'
});
}
return res.status(500).json({
success: false,
message: 'Wrong data'
});
}
console.log('is authenticated?: ' + req.isAuthenticated()) // Here always is false
return res.json({
success: true,
token,
message: 'Successful Login',
user: userData.data
});
})(req, res, next);
});
// I am using this function as a middleware to check if the user is authenticated, always is false. No matter if I put right data in the login form
function ensureAuth (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.status(401).send({ error: 'not authenticated' })
}
auth/index.js(passport routine)
var LocalStrategy = require('passport-local').Strategy;
var LisaClient = require('pos_lisa-client');
var config = require('../config');
var ClientApi = LisaClient.createClient(config.lisaClient);
exports.localStrategy = new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: false,
passReqToCallback: true
}, (req, username, password, done) => {
var authData = {
username,
password
}
// Here is a custom API, I pass the data that I need. This works fine
ClientApi.authenticate(authData, (err, token) => {
if (err) {
return done(err)
}
var token = token.data
ClientApi.getClient(username, (err, user) => {
if (err) {
return done(err)
}
user.token = token
return done(null, user)
})
})
})
exports.serializeUser = function (user, done) {
// The app never enters here
done(null, {
username: user.username,
token: user.token
})
}
exports.deserializeUser = function (user, done) {
// The app never enters here
ClientApi.getClient(user.username, (err, usr) => {
if (err) {
return done(null, err)
} else {
usr.token = user.token
done(null, usr)
}
})
}
Where I am wrong?
If you're using a custom authentication callback, you have to call req.logIn() to establish a session (or you can create one manually):
// Add this where you are console.log'ing `req.isAuthenticated()`
req.logIn(userData, function(err) {
if (err) return next(err);
console.log('is authenticated?: ' + req.isAuthenticated());
return res.json({
success: true,
token,
message: 'Successful Login',
user: userData.data
});
});
This is documented here (scroll down to "Custom Callback"):
Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response.

Resources