I have used jwt token to login
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
.
Below is my code for router
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
// validate
if (!email || !password)
return res.status(400).json({ msg: "Not all fields have been entered." });
const user = await Customer.findOne({ email: email });
if (!user)
return res
.status(400)
.json({ msg: "No account with this email has been registered." });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ msg: "Invalid credentials." });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
res.json({
token,
user: {
id: user._id,
displayName: user.displayName,
},
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
Can anybody provide code for loging out using jwt token
You just need to invalidate your jwt token in logout
more than one way you can achieve. Here I am going to explain a couple of ways
1.storing token in an array. In log out, you can remove the token
const refreshTokens = []; --> global declaration
in login, before res.json({...});
refreshTokens.push(refreshToken);
the constraint here is jwt tokens are time-bounded. You need to get a refresh token if old token expires. Whenever you issue a refresh token you need to remove the old and push the latest
router.post('/refreshtoken', function (req, res) {
const { token } = req.body;
if (!token) {
return res.sendStatus(401);
}
if (!refreshTokens.includes(token)) {
return res.sendStatus(403);
}
jwt.verify(token, refreshTokenSecret, (err, user) => {
if (err) {
return res.sendStatus(403);
}
const accessToken = jwt.sign({ username: user.username, role: user.role }, accessTokenSecret, { expiresIn: '20m' });
refreshTokens = refreshTokens.filter(token => t !== token);
refreshTokens.push(accessToken);
res.json({
accessToken
});
});
});
In Logout you need to invalidate token
app.post('/logout', (req, res) => {
const { token } = req.body;
refreshTokens = refreshTokens.filter(token => t !== token);
res.send("Logout successful");
});
2.Store token in cookie whenever you log in or reissues the token. verify jwt token from cookie instead of reading from headers.
res.cookie('jwt_token', token, {
expires: new Date(Date.now() + expiration),
secure: false, // set to true if your using https
httpOnly: true,
});
In Logout destroy the cookie
router.get('/logout', function (req, res) {
res.clearCookie('jwt_token');
req.session.destroy();
});
Related
I have a website where when user logsIn, they are assigned an access and a refresh token. When the access token is expried, a request to the server is made and checks if the refresh token is present in the global array in the database. If it is, a new access token is assigned to the user.
But I wanted to ask if should also check for the user by the information given by the refresh token when it is decoded. Or it is not necessary.
Please suggest me good practice and also tell me if something is wrong with my process.
routes.post("/newAccessToken", async (req, res) => {
const token = req.headers.cookie?.split("=")[1];
try {
const existingToken = await refreshTokens.findOne({
tokens: { $in: [token] },
});
if (existingToken) {
const email = await jwt.verify(token, process.env.RefreshTokenSecret);
if (email) {
const user = await userSchema.findOne({ email });
if (user) {
const newAccessToken = await jwt.sign(
{ email },
process.env.AccessTokenSecret
);
res.json({ newAccessToken });
}
} else res.json({ message: "token is invalid" });
} else res.json({ message: "No token found" });
} catch (error) {
console.log(error);
}
});
I am trying to authenticate users to my app using Facebook. I made sure that the configuration on https://developers.facebook.com/apps/1137899716785088/settings/basic/ is as it should ie: I added http://localhost:3000 as an app domain (after clicking save it still got converted to "localhost" only, but I guess that is okay. The error I get when loading my website is:
throw new Error(msg);
^
Error: Route.get() requires a callback function but got a [object Object]
I have already checked these threads: 1, 2, 3 but none of them solved my issue. Below is my code
passport/facebook.js
const passport = require('passport');
const facebookStrategy = require('passport-facebook').Strategy;
const User = require('../models/user');
const keys = require('../config/keys');
// fetch user ID and generate cookied ID for browser
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use(new facebookStrategy({
clientID: keys.FBAppID,
clientSecret: keys.FBAppSECRET,
callbackURL: 'http://localhost:3000/auth/facebook/callback',
profileFields: ['email', 'name', 'displayName', 'photos']
// profileFields: ['id', 'email', 'gender', 'link', 'locale', 'name', 'timezone', 'updated_time', 'verified'],
}, (accessToken, refreshToken, profile, done) => {
console.log(profile);
// save user data
User.findOne({facebook: profile.id}, (err, user) => {
if (err) {
return done(err);
}
if (user) {
return done(null, user);
} else {
const newUser = {
facebook: profile.id,
firstname: profile.name.givenName,
lastname: profile.name.familyName,
image: `https://graph.facebook.com/${profile.id}/picture?type=large`,
email: profile.emails[0].value
}
new User(newUser).save((err, user) => {
if (err) {
return done(err);
}
if (user) {
return done(null, user);
}
})
}
})
}));
app.js
// load modules
const express = require('express');
const passport = require('passport');
// init app
const app = express();
app.use(passport.initialize())
app.use(passport.session());
// load passports
require('./passport/local');
require('./passport/facebook');
//passport authentication
app.get('/auth/facebook', passport.authenticate('facebook'), {
scope: ['email']
});
app.get('/auth/facebook/callback', passport.authenticate('facebook', {
successRedirect: '/profile',
failureRedirect: '/'
}));
The mistake was a syntactical one. I accidentally closed the round brackets immediately after the 'facebook' part, when in fact I shouldn't have done that.
Before fix:
app.get('/auth/facebook', passport.authenticate('facebook'), {scope: ['email']});
After fix:
app.get('/auth/facebook', passport.authenticate('facebook', {scope: ['email']}));
here is my code, this is the middleware I'm using to verify jwt token, but it's not working at all
const verifyJWT = (req, res, next) => {
const accessToken = req.headers.authorization;
if (!accessToken) {
res.status(401).send({ message: "Unauthorized Access" });
return;
}
const token = accessToken.split(" ")[1];
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decoded) => {
if (err) {
res.status(403).send({ message: "Forbidden Access" });
return;
}
req.decoded = decoded;
next();
});
};
//this is my code
I'm have an express server with MongoDB and Mongoose, and using passport to authenticate with JWT, but getting an error as in the title.
I'm following the passport-jwt documentation, but am still getting the error. What am I doing wrong?
Here is the error message when doing GET call on localhost3090 with a valid JWT:
::1 - - [16/Mar/2018:05:35:47 +0000] "GET / HTTP/1.1" 500 1677 "-" "PostmanRuntime/7.1.1"
Error: passport.initialize() middleware not in use
at IncomingMessage.req.login.req.logIn (/Users/okadachikara/react-courses/projects/server/node_modules/passport/lib/http/request.js:46:34)
at JwtStrategy.strategy.success (/Users/okadachikara/react-courses/projects/server/node_modules/passport/lib/middleware/authenticate.js:248:13)
at verified (/Users/okadachikara/react-courses/projects/server/node_modules/passport-jwt/lib/strategy.js:115:41)
at /Users/okadachikara/react-courses/projects/server/services/passport.js:34:7
at /Users/okadachikara/react-courses/projects/server/node_modules/mongoose/lib/model.js:3930:16
at _init (/Users/okadachikara/react-courses/projects/server/node_modules/mongoose/lib/query.js:2007:5)
at model.Document.init (/Users/okadachikara/react-courses/projects/server/node_modules/mongoose/lib/document.js:393:5)
at completeOne (/Users/okadachikara/react-courses/projects/server/node_modules/mongoose/lib/query.js:1993:12)
at Immediate.<anonymous> (/Users/okadachikara/react-courses/projects/server/node_modules/mongoose/lib/query.js:1520:11)
at Immediate._onImmediate (/Users/okadachikara/react-courses/projects/server/node_modules/mquery/lib/utils.js:119:16)
at runCallback (timers.js:773:18)
at tryOnImmediate (timers.js:734:5)
at processImmediate [as _immediateCallback] (timers.js:711:5)
My server/controllers/authentication.js:
const User = require('../models/user');
const jwt = require('jwt-simple');
const config = require('../config');
function tokenForUser(user) {
const timestamp = new Date().getTime();
return jwt.encode({ sub: user.id, iat: timestamp }, config.secret);
}
exports.signup = function (req, res, next) {
const email = req.body.email;
const password = req.body.password;
if (!email || !password) {
return res.status(422).send({ error: 'You must provide an email and
password' });
}
// see if user with the given email exists
User.findOne({ email: email }, function (err, existingUser) {
if (err) { return next(err); }
if (existingUser) {
return res.status(422).send({ error: 'A user with that email
already exists' });
}
const user = new User({
email: email,
password: password
});
user.save(function (err) {
if (err) { return next(err); }
res.json({ token: tokenForUser(user), iat: jwt.iat });
});
});
};
My server/services/passport.js
const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/user');
const config = require('../config');
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'),
secretOrKey: config.secret
};
const jwtLogin = new JwtStrategy(jwtOptions, function (payload, done) {
User.findById(payload.sub, function (err, user) {
if (err) { return done(err, false); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
passport.use(jwtLogin);
My server/router.js
const passport = require('passport');
const Authentication = require('./controllers/authentication');
const passportService = require('./services/passport');
const requireAuth = passport.authenticate('jwt', { sesssion: false });
module.exports = function (app) {
app.get('/', requireAuth, function (req, res) {
res.send({ hi: 'there' });
});
app.post('/signup', Authentication.signup);
};
You need to initialize the passport module before using it:
let app = express();
app.use(passport.initialize());
Everything was working fine until I decided to save the tokens (as String) with the users. The tokens were used to be 5 lines long max, and now it is keep growing every time I refresh the token. The last token I generated was 100 lines long which is not acceptable.
Every time the user logs in, I am refreshing the token.
module.exports.login = function(req, res){
var user_name = req.body.username;
var password = req.body.password;
User.findOne({username: user_name}, function(err, user){
if(err || !user) {
return res.json({error: "cannot find the user"});
} else{
user.comparePassword(password, function(err, isMatch){
if (err){
return res.json({
error: "passowrd doesn't match"
});
}
});
var token = jwt.sign(user, process.env.SECRET, {
expiresIn: 4000
});
console.log(token); // printing the token
}
if(!token){
res.json({
success: false,
username: null,
token: null
});
}
else {
user.token = token;
User.updateUser(user._id, user, {new: true},function(err, updated_user){
res.json({
success: true,
username: user.username,
token: token
});
});
}
});
};
All the routes are secured, and it needs to verify the token for each request.
module.exports.secured = function(req, res, next){
var token;
var username = req.body.req_username || req.headers['req_username'];
if(username){
User.findOne({ 'username': username }, function (err, user) {
if (err || !user)
return res.json({
error: "cannot find the user"
});
else
token = user.token;
jwt.verify(token, process.env.SECRET, function(err, decode){
if(err){
res.status(500).send({
error: "wrong token or username"
});
} else{
next();
}
});
});
} else{
res.send({
error: "not found"
});
}
};
I think I am not refreshing the tokens correctly.
The token should not be stored in the server because it wastes unnecessary resources.
user.token = token;
User.updateUser(user._id, user, {new: true},function(err, updated_user){
res.json({
success: true,
username: user.username,
token: token
});
});
Each time the token is refreshed you encode the user variable that includes the previously issued token, therefore it grows
var token = jwt.sign(user, process.env.SECRET, {
expiresIn: 4000
});
Remove user.token = token
But the main issue is that the client MUST send the JWT in each request, not the username to recover the token. You are verifying the token stored in user entity. It does not make sense. Change the client code to.send the JWT in the headers instead of the username req.headers['req_username']