I am learning how to use NodeJS, and while following an online tutorial I incurred in the error in the title.
I am using the latest version of NodeJS, and the jsonwebtoken authentication.
This is how I generate the token:
router.post('/login', (req, res) => {
// Check if username was provided
if (!req.body.username) {
res.json({
success: false,
message: 'No username was provided'
}); // Return error
} else {
// Check if password was provided
if (!req.body.password) {
res.json({
success: false,
message: 'No password was provided.'
}); // Return error
} else {
// Check if username exists in database
User.findOne({
username: req.body.username.toLowerCase()
}, (err, user) => {
// Check if error was found
if (err) {
res.json({
success: false,
message: err
}); // Return error
} else {
// Check if username was found
if (!user) {
res.json({
success: false,
message: 'Username not found.'
}); // Return error
} else {
const validPassword = User.comparePassword(req.body.password, user.password); // Compare password provided to password in database
// Check if password is a match
if (!validPassword) {
res.json({
success: false,
message: 'Password invalid'
}); // Return error
} else {
const token = jwt.sign({
userId: user._id
}, 'goodsecret', {
expiresIn: '24h'
}); // Create a token for client
res.json({
success: true,
message: 'Success!',
token: token,
user: {
username: user.username
}
}); // Return success and token to frontend
}
}
}
});
}
}
});
And here how I evaluate it:
const jwt = require('jsonwebtoken');
/* ================================================
MIDDLEWARE - Used to grab user's token from headers
================================================ */
router.use((req, res, next) => {
const token = req.headers.authorization; // Create token found in headers
// Check if token was found in headers
if (!token) {
res.json({
success: false,
message: 'No token provided'
}); // Return error
} else {
// Verify the token is valid
console.log(token);
jwt.verify(token, 'goodsecret', (err, decoded) => {
// Check if error is expired or invalid
if (err) {
res.json({
success: false,
message: 'Token invalid: ' + err
}); // Return error for token validation
} else {
req.decoded = decoded; // Create global variable to use in any request beyond
next(); // Exit middleware
}
});
}
});
/* ===============================================================
Route to get user's profile data
=============================================================== */
router.get('/profile', (req, res) => {
// Search for user in database
User.findOne({
_id: req.decoded.userId
}).select('username email').exec((err, user) => {
// Check if error connecting
if (err) {
res.json({
success: false,
message: err
}); // Return error
} else {
// Check if user was found in database
if (!user) {
res.json({
success: false,
message: 'User not found'
}); // Return error, user was not found in db
} else {
res.json({
success: true,
user: user
}); // Return success, send user object to frontend for profile
}
}
});
});
Whenever I try to generate a request from HttpRequester, sending the token, I get the error in the title. The token I am sending is
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1OWRlMTYzNTMwOWY2NDM1YjBjOWRmM2UiLCJpYXQiOjE1MDc3NDMzODYsImV4cCI6MTUwNzgyOTc4Nn0.uJwpYN7IHYg_lmCVCpFg-zfo0QVPglEvWHs7SD9cPkg
and on https://jwt.io/ it works fine, using the secret 'goodsecret' as in the code. What am I missing?
This is a screen on how I am generating requests.
Thank you for your help
Related
I'm trying to learn JWT authentication in express and one thing that I'm came across this code from Github
that this guy has initialised an middleware function to authenticate and check expiry of access token as per below:
app.post("/protected", auth, (req, res) => {
return res.json({ message: "Protected content!" });
})
async function auth(req, res, next) {
let token = req.headers["authorization"];
token = token.split(" ")[1]; //Access token
jwt.verify(token, "access", async (err, user) => {
if (user) {
req.user = user;
next();
} else if (err.message === "jwt expired") {
return res.json({
success: false,
message: "Access token expired"
});
} else {
console.log(err);
return res
.status(403)
.json({ err, message: "User not authenticated" });
}
});
}
and a separate route for refreshing the access token with the help of refresh token
app.post("/refresh", (req, res, next) => {
const refreshToken = req.body.token;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: "Refresh token not found, login again" });
}
// If the refresh token is valid, create a new accessToken and return it.
jwt.verify(refreshToken, "refresh", (err, user) => {
if (!err) {
const accessToken = jwt.sign({ username: user.name }, "access", {
expiresIn: "20s"
});
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: "Invalid refresh token"
});
}
});
});
So, my question is how secure it is and how can I create single middleware function that could do both authentication and refreshing access token without hitting the app.post('/refresh') as in my view it wouldn't be a smooth experience to deal with it in frontend API management within react
Edit
My middleware seems to work well but it doesn't identify the wrong refresh token and then actually getting worked on protected route
app.post('/home', authenticateUser, (req, res) => {
res.send('welcome');
});
async function authenticateUser(req, res, next) {
let token = req.headers['authorization'];
token = token.split(' ')[1];
jwt.verify(token, JWT_AUTH_TOKEN, async (err, phone) => {
if (phone) {
req.phone = phone;
next();
} else if (err) {
const refreshToken = req.body.refreshToken;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: 'Refresh token not found, login again' });
} else {
jwt.verify(refreshToken, JWT_REFRESH_TOKEN, (err, phone) => {
if (!err) {
const accessToken = jwt.sign({ phone }, JWT_AUTH_TOKEN, { expiresIn: '30s' });
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: 'Invalid refresh token'
});
}
next();
});
}
} else {
console.log(err);
return res.status(403).json({ err, message: 'User not authenticated' });
}
});
}
I am not getting any response while calling login api from nodejs.
I am handling the catch in frontend as well.
How to get Invalid Credentials message from backend API if credentials doesn't matched.
my Backend login API is -
// api to login user
router.post('/login', function (req, res) {
const valid = validator.isEmail(req.body.email)
if (valid == false) {
var message = { "Success": 0, "Message": "Please enter a valid email." };
res.send(message)
}
userObj.findOne({
email: req.body.email
}).then(user => {
if (!user) {
var message = { "Success": 0, "Message": "User does not exists." };
res.send(message)
} else {
// console.log(bcrypt.compare(req.body.password, user.password))
// var message = { "Success": 1, "User": user };
// res.send(message)
bcrypt.compare(req.body.password, user.password)
.then(isMatch => {
if (isMatch) {
const payload = {
name: user.name,
id: user._id,
email: user.email
}
jwt.sign(payload, 'secret', {
expiresIn: 3600
}, (err, token) => {
if (err) console.error('There is some error in token', err);
else {
res.json({
Success: 1,
token: `${token}`
})
}
})
}
else {
res.json({
Success: 0,
Message: 'Invalid Credentials'
})
}
})
}
})
});
my frontend login action code is -
// Login - get user token
export const loginUser = user => dispatch => {
return axios
.post("http://18.207.190.61:4000/login", {
email: user.email,
password: user.password
})
.then(res => {
// Save to localStorage
// Set token to localStorage
localStorage.setItem("usertoken", res.data.token);
// Set token to Auth header
setAuthToken(res.data.token);
// Decode token to get user data
const decoded = jwt_decode(res.data.token);
// Set current user
localStorage.setItem("username", decoded.name);
dispatch(setCurrentUser(decoded));
return res.data;
})
.catch(err => {
return err;
});
};
finally my login component code is -
this.props.loginUser(user).then((res, err) => {
if (res.Success == "0") {
this.setState({
loading: false,
message: res.Message
});
}
});
How can I get message Message: 'Invalid Credentials' from backend API in front end to print.
Please return response with status codes '200' for success and '401' for invalid credentials and try again. Axios recognises the status codes and tells if there is an error.
if(success)
res.status(200).json({
Success: 1,
token: '${token}'
})
else
res.status(401).json({
Success: 0,
Message: 'Invalid Credentials'
})
Try this once.
1.Remove the catch block in your login action code
2. change your login component code to
this.props.loginUser(user).then((res ) => {
if (res.Success == "0") {
this.setState({
loading: false,
message: res.Message
});
}
});
I've implemented passport local strategy using async/await as below
const strategy = new LocalStrategy(
async(username, password, done) => {
try {
// Find the user given the username
const user = await User.findOne({ username });
// If not, send info
if (!user) {
return done(null, false, {
success: false,
message: 'User not found'
})
}
// Check if the password is correct
const isMatch = await user.isValidPassword(password);
// If not, send info
if (!isMatch) {
return done(null, false, {
success: false,
message: 'Invalid Password'
});
}
// Otherwise, return the user
done(null, user);
} catch (error) {
done(error, false);
}
}
);
passport.use(strategy);
And implemented custom callback in routes using the code below.
router.post('/login', async(req, res, next) => {
const { receivedUser, information } = await passport.authenticate('local');
// If a user is found
if (receivedUser) {
res.status(200).json({
success: true,
message: 'Authentication Successful'
});
} else {
// If user is not found
res.status(401).json(information);
}
};
);
There are errors in above custom callback implementation as receivedUser and information are 'undefined'. How to make changes to above custom callback using async/await to remove errors ?
Referred docs:
http://passportjs.org/docs/configure
http://passportjs.org/docs/username-password
I am developing a android aplication with nodejs and postgreSQL, at the moment i just have the login and the register.
When i do a login and everything is fine the server send me a token, that token is stored on the device SharedPreference, now my confusion is, do i need to decode this token on every request, or do i need to do it just 1 time?
in this tutorial at the end, he decodes on every route the token, but i don't need to do that when i do for example a request to register.
What is the best way to implement this?
here is my server code:
//****************************************************Begin of login request **********************************/
router.post('/login', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
// search a user to login
User.findOne({ where: { username: req.body.username } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if (user && user.validPassword(req.body.password)) {
//return res.status(200).json({ message: "loged in!" }); // username and password match
var payload = { user: user };
// create a token
var token = jwt.sign(payload, 'superSecret', {
expiresIn: 60 * 60 * 24
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
else {
return res.status(401).json({ message: "Unauthorized" }); // if there is no user with specific fields send
}
}).catch(function (err) {
console.error(err.stack)
return res.status(500).json({ message: "server issues when trying to login!" }); // server problems
});
});
//****************************************************End of Login request **********************************/
//****************************************************Begin of register request******************************/
router.post('/register', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Register request body is empty" });
}
if (!req.body.email || !req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for registration" });
}
var password = User.generateHash(req.body.password);
User.create({
username: req.body.username,
email: req.body.email,
password: password
}).then(function () {
return res.status(200).json({ message: "user created" });
}).catch(function (err) {
return res.status(400).send({ message: err.message }); //
}).catch(function (err) {
return res.status(400).json({ message: "issues trying to connect to database" });
})
});
//****************************************************End of register request **********************************/
module.exports = router;
If you don't want to use JWT token check for all routes, you can skip those routes.
const url = require('url');
apiRoutes.use((req, res, next) => {
const path = url.parse(req.url).pathname;
console.log(path);
//No JWT token check
if (/^\/register/.test(path)) {
return next();
}
return jwtTokenValidate();
});
function jwtTokenValidate() {
// 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, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} 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
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
I'm creating an application with nodejs and passport-local-mongoose,
the problem is that i cannot find a way to update the user password since passport use Salt and Hash, there's some method or some way to update the password by a PUT method?
Assuming you've added the passport-local-mongoose plugin to your user schema, you should be able to call
setPassword(password, cb) on your user schema.
yourSchemaName.findById(id, function(err, user) {
user.setPassword(req.body.password, function(err) {
if (err) //handle error
user.save(function(err) {
if (err) //handle error
else //handle success
});
});
});
If you want to change the password you can use changePassword command.
here is an example
router.post('/changepassword', function(req, res) {
// Search for user in database
User.findOne({ _id: 'your id here' },(err, user) => {
// Check if error connecting
if (err) {
res.json({ success: false, message: err }); // Return error
} else {
// Check if user was found in database
if (!user) {
res.json({ success: false, message: 'User not found' }); // Return error, user was not found in db
} else {
user.changePassword(req.body.oldpassword, req.body.newpassword, function(err) {
if(err) {
if(err.name === 'IncorrectPasswordError'){
res.json({ success: false, message: 'Incorrect password' }); // Return error
}else {
res.json({ success: false, message: 'Something went wrong!! Please try again after sometimes.' });
}
} else {
res.json({ success: true, message: 'Your password has been changed successfully' });
}
})
}
}
});
});
If you want to change the password without using the old password you can use setPassword method. Here is an example
user.setPassword(req.body.password, function(err,user){
if (err) {
res.json({success: false, message: 'Password could not be saved. Please try again!'})
} else {
res.json({success: true, message: 'Your new password has been saved successfully'})
}
});