How do I logout with JWT, passport Authentication (NodeJS)? - javascript

I am new to NodeJS and I have been following a tutorial. I made some changes to the tutorial code in order to allow users to login by typing in their username or email instead of just username.
passport.use(
new LocalStrategy(
(username_or_email, password, done) => {
User.findOne({ email: username_or_email }, (err, user) => {
if (err) {
return done(err);
console.log(err);
}
if (!user) {
User.findOne({ username: username_or_email }, (err, user) => {
//If there is an error
if (err) {
return done(err);
console.log(err);
}
//If there is no user
if (!user) {
return done(null, false);
} else {
user.comparePassword(password, done);
}
});
} else {
user.comparePassword(password, done);
}
});
}
)
The code above works well and allows users to type in username OR password to login.
Now when I follow the tutorial for how to logout, their method doesn't work for me.
I have a route like this, it is supposed to log a user out.
userRouter.get(
'/logout',
passport.authenticate('jwt', { session: false }),
(req, res) => {
res.clearCookie('access_token');
res.json({ user: { username: '', role: '' }, success: true });
}
);
When I go to it in Postman it says "Unauthorized" and does not return anything else.
I believe it could be something to do with my 'jwt' set up, shown below.
passport.use(
new JwtStrategy(
{
jwtFromRequest: cookieExtractor,
secretOrKey: 'NoobCoder',
},
(payload, done) => {
console.log(payload);
User.findById({ _id: payload.sub }, (err, user) => {
if (err) {
return done(err, false);
console.log('1');
}
if (user) {
return done(null, user);
console.log('2');
} else {
return done(null, false);
console.log('3');
}
});
}
)
);
This is the cookieExtractor function that I use for jwtFromRequest
const cookieExtractor = (req) => {
let token = null;
console.log(token);
if (req && req.cookies) {
token = req.cookies['access_token'];
}
return token;
};
The only console output I get is the console.log in the cookieExtractor.
Which makes me believe that that must be the point of failure. It is a "null" output as expected, and if I console.log the token, I get the current logged in users token. I believe the jwtFromRequest calls the cookieExtractor function but fails at some point soon after.

Related

How to use access messages from your passportjs strategy in React

If you have a react application and don't wanna use connect-flash to access the messages generated by the local strategy you can use the following:
My passport local strategy, I am using a sqlite db:
passport.use(
new LocalStrategy({ usernameField: 'name' },
(name, password, done) => {
//Match user
db.get(`SELECT * FROM Users WHERE name = '${name}'`, (err, user) => {
if(err) throw err;
if(!user) {
return done(null, false, { message : 'Username not registered' });
}
//Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if(err) throw Error;
if(isMatch) {
done(null, user, { message : 'Log in successful' });
} else { done(null, false, { message : 'Password incorrect' })
}
});
})
})
);
You can use a custom call back function for your /login route:
app.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) { return next(err); }
if (!user) { return res.json(info); }
req.logIn(user, (err) => {
if (err) { return next(err); }
return res.json(info);
});
})(req, res, next);
});
The info argument contains the object passed as a third argument in done() from the strategy and can be added to the body of res and therefore used by react.

Passport login with React Router only working on second click?

We're trying to integrate Passport authentication with out React app, and we're using React Router.
On the first submission of correct user credentials, the server receives the post, adds a session to our database, and seems to send a response, but the client doesn't update. The username and password show up in the url as a query string. Then when we resend the credentials without removing the query string from the url, the client is able to receive the response from the server.
In other words, if we don't refresh before submitting the login info again, it works.
This is the click handler that our form utilizes:
const handleClick = () => {
return axios.post('/login', { username, password })
.then(({ data }) => {
const { message } = data;
if (message === 'success') {
const { user } = data;
setUserId(user.id);
setUser(user);
}
setAuthStatus(message);
})
.catch(err => console.error(err));
};
This is our server route that is hit on every post request:
loginRouter.post('/', (req, res, next) => {
console.log('stop');
passport.authenticate('local', (err, user, info) => {
const { invalidPassword } = info || false;
if (err) {
return next(err); // will generate a 500 error
}
if (!user) {
return res.send({ message: 'invalidUser' });
}
if (invalidPassword) {
return res.send({ message: 'invalidPassword' });
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ user, message: 'success' });
});
})(req, res, next);
});
This is our Passport Local Strategy that uses Sequelize:
passport.use(new LocalStrategy(
(username, password, cb) => {
User.findOne({ where: { username } })
.then((user) => {
if (!user) {
return cb(null, false);
}
if (validPassword(password, user.hash, user.salt)) {
return cb(null, user);
}
return cb(null, false, { invalidPassword: true });
})
.catch(err => {
cb(err);
});
},
));
Having trouble debugging this... We suspect the error is on the client side and may have to do with React-Router. We are using React-Router and Passport for the first time on this project.
Any help is appreciated!
Welp... All we were missing was event as a parameter in handleClick and event.preventDefault().

Passport jwt returns unauthorized with status code 401

passport.js
module.exports = (passport) => {
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
(username, password, done) => {
user.findOne(({ username: username }), async (err, user) => {
if (err) { return done(err); }
if (!user) { return done(null, false); }
const cpassword = await bcrypt.compare(password, user.password);
if (!cpassword) { return done(null, false); }
console.log(user._id)
const token = jwt.sign(
{
id: user._id,
username: user.username
},
"shubham"
);
adminRecords = {
token: token
}
return done(null, user, adminRecords);
});
}
));
passport.use(new JwtStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey : 'key'
}, function(jwt_payload, done) {
console.log('1')
user.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
// or you could create a new account
}
});
}));
};
passport.serializeUser(function (user, done) {
done(null, user)
})
passport.deserializeUser(function (id, done) {
user.find(id, function (err, user) {
done(err, user)
});
});
route.js
router.post('/profile', passport.authenticate('jwt', { session: false }),
function(req, res) {
console.log('1')
res.send(req.user.profile);
}
);
I am trying passport-jwt strategy and it returns unauthorized with status code 401 whereas token is generating. I am using token x-acccess-token=jwtToken but it returns unauthorized.
What can I try to debug this?
try to set in the header
Authorization: Bearer TOKEN
you should have space between token and bearer
replace the jwtFromRequest: as below and try out
jwtFromRequest:ExtractJwt.fromAuthHeaderWithScheme('JWT');

passport-jwt returns unauthorized when I go to /admin page on my web app

I am trying to get the user logged in using passport and jwt token. I manage to generate jwt token successfully but when I go to /admin page by typing in the browser I am getting the unauthorized message. I have read all the answer here but they aren't helping me out.
My passport.js file
try {
passport.use('signin', new LocalStrategy(
function(username, password, done) {
Users.findOne({username: username}, (err, user) => {
if (err) { return(err)}
if(!user) { return done(null, false, { message: 'Incorrect username.' })}
if (!checkUser(password, user.password)) { return done(null, false, { message: 'Incorrect username.' }); }
return done(null, user);
});
}
))
} catch (error) {
return done(error);
}
passport.use(new JWTstrategy({
secretOrKey: config.get('jwt.tokenKey'),
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
}, async(token, done) => {
try {
return done(null, token.user);
} catch (err){
done(err);
}
}))
My login-controller file: -
router.post('/', (req, res) => {
passport.authenticate('signin', {session:false}, async(err,user,info) => {
try {
if (err || !user) {
console.log(err);
}
req.login(user, {session:false}, err => {
if (err) res.send(err);
const body = { username: user.username};
const token = jwt.sign( {user:body}, config.get('jwt.tokenKey'));
res.json({ success: true,
token: 'Bearer ' + token });
});
} catch (error) {
return next(error);
}
})(req, res);
})
My admin page file: -
router.get('/', passport.authenticate('jwt', { session: false }), function (req,res) {
res.render('./../views/admin/admin.pug');
})

How to set isAdmin variable in passport local strategy?

I am trying to create a middleware to allow only admin access to certain routes in my node.js project. My middleware currently looks like this.
function adminAuth(req, res, next){
if(req.user.isAdmin){
return next();
} else {
res.redirect("/");
}
}
The issue is that req.user.isAdmin is never recognized even if the user data contains isAdmin: true. I believe that I need to define user.isAdmin in my passport strategy but I am not sure how to do it. Any help would be greatly appreciated!
here is my local strategy
(async function addUser() {
let client;
try {
client = await MongoClient.connect(url);
const db = client.db(dbName);
const col = db.collection('users');
const user = await col.findOne({ email });
debug('Found user by email');
debug(user);
if (!user) {
req.flash('error', 'The username or password is wrong');
done(null, false);
} else {
const match = await bcrypt.compare(password, user.password);
if (match) {
done(null, user);
} else {
req.flash('error', 'The username or password is wrong');
// we pass null because it did not error, just failed
done(null, false);
}
}
} catch (e) {
debug(e.stack);
}
client.close();
}());
}
));
};
Here is my passport.js
passport.serializeUser((user, done) => {
done(null, user.email);
});
passport.deserializeUser((email, done) => {
done(null, email);
});
};

Categories