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']
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);
}
});
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 using Oauth to allow my users to sign in with their google account. I have successfully created a function which checks if the email is verified and then, depending on if the user is already saved in the data base, just send back the user - or if the user doesn't exsist create a new user.
My console displays the message "Email is verified" and "User not found" so I know it makes it to that part of the code - but it doesn't proceed to create a new user. My suspicions is that is has to do with the async await and that things are perhaps happening in the wrong order. Any thoughts would be much appreciated!
app.post("/googlelogin", (req, res) => {
const { tokenId } = req.body
client.verifyIdToken({
idToken: tokenId,
audience: 'XXXX' // removed for demo
})
.then(async (response) => {
const { email_verified, name, email } = response.payload
console.log(response.payload)
if (email_verified) {
console.log('email is verified')
try {
const user = await User.findOne({ name, email })
if (user) {
console.log('User found!')
return res.json({
success: true,
name: user.name,
email: user.email,
token: user.token,
userID: user._id
})
} else {
console.log('User not found!')
let newUser = await new User({
name,
email
}).save()
return res.json({
success: true,
name: newUser.name,
email: newUser.email,
token: newUser.token,
userID: newUser._id
})
}
} catch (error) {
res.status(400).json({
success: false,
message: "Something went wrong",
error
})
}
} else {
return res.status(400).json({
success: false,
error: "Email not verified",
})
}
})
})
SOLVED.
Found this thread and followed the tip to drop my collection and after this it worked!
MongoError: E11000 duplicate key error collection: tracker-db.users index: username_1 dup key: { username: null }"
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();
});
The authentication process gives 401 bad request. Though the new user is saved to db with hashed password I noticed is saved to MongoDB Atlas. I'm using the local strategy. If I try "local-signup" for the register t I reach the secret route, but for the login route I still get the 401 bad request.
Or can there be an authenticaiton issue with mongodb atlas trying to access the credentials??
app.post("/register", function (req, res, next) {
var newUser = new User({
username: req.body.username
});
User.register(newUser, req.body.password, function (err, user, info) {
console.log(user);
if (err) {
return res.render("register");
} else {
// go to the next middleware
next();
}
res.status(401).send(info);
});
}, passport.authenticate('local', {
successRedirect: '/secret',
failureRedirect: '/login'
}));
app.post(
"/login",
passport.authenticate("local", {
successRedirect: "/secret",
failureRedirect: "/login"
}),
function (request, response) {}
);
Found the error. my misstake
passport.use(new localStrategy(User.authenticate));
shold be
passport.use(new localStrategy(User.authenticate()));