I have an async function for a login call in a controller file, it looks something like this:
login: async(req, res) => {
try {
const {email, password} = req.body
const user = await Users.findOne({email})
if(!user) return res.status(400).json({msg: "This email does not exist."})
const isMatch = await bcrypt.compare(password, user.password)
if(!isMatch) return res.status(400).json({msg: "Incorrect password. Please try again."})
console.log(user)
const payload = {id: user._id, name: user.name}
const token = jwt.sign(payload, process.env.TOKEN_SECRET, {expiresIn: "7d"})
res.cookie('token', token, {
httpOnly: true,
maxAge: 7*24*60*60*1000 //7 days
})
localStorage.setItem('token', JSON.stringify(token));
res.json({msg: "Login successful", token})
} catch (error) {
return res.status(500).json({msg: err.message})
}
I'm trying to store the JWT that's been generated into localStorage for retrieval on subsequent API calls. I've gotten it so that I can see that the cookie that's been created is there every time I go to my local dev server.
Currently, upon login, this throws a ReferenceError and I'm unsure why.
Error: return res.status(500).json({msg: err.message})
^
ReferenceError: err is not defined
What's the proper way of putting a generated token into localStorage?
Related
I've created a user login back end and everything works fine, but when I log in to a user detail, despite being correct, I'm unable to explore other routes because the user isn't authorized. how do I save the access token to the browser so it remembers? This is the login route below.
router.post("/login", async (req, res) => {
try {
const oneUser = await users.findOne({
username: req.body.username,
});
if (!oneUser) {
return res.status(500).json("User is not in the database");
}
const oPassword = cj.AES.decrypt(
oneUser.password,
process.env.pass
).toString(cj.enc.Utf8);
if (oPassword !== req.body.password) {
return res.status(500).json("Password is incorrect");
}
const accessToken = jwt.sign(
{
id: oneUser._id,
isAdmin: oneUser.isAdmin,
},
process.env.jwtToken,
{ expiresIn: "300" }
);
const { password, ...others } = oneUser._doc;
res.status(200).json({ ...others, accessToken });
} catch (err) {
res.status(500).json(err);
}
});
I saw something like this on the net.
res.header("token", accessToken)
for this you can save a session for that user or response the token and save that to the client browser cookies or session store.
for setting session at node js app checkout link below
https://www.npmjs.com/search?q=session
I have a auth.js file And a middleware named as fetchuser code given beolow
Can anyone please tell me why am i getting this error.
I am using express js and mongoose but this error is occured during sending token to the user and verify the user whether is user logged in or not.
auth.js
const express = require('express');
const User = require('../models/User');
const router = express.Router();
const { body, validationResult } = require('express-validator');
const bcrypt = require('bcryptjs'); // it is used for password hashing
const jwt = require('jsonwebtoken');
const fetchuser=require('../middleware/fetchuser');
// Route:1 - Create a User using :POST. "/api/auth/createuser". NO Login Required.
router.post('/createuser', [
body('email', 'Enter valid email').isEmail(),
body('name', 'Enter valid email').isLength({ min: 3 }),
body('password').isLength({ min: 5 })
], async (req, res) => {
// Check fo vaidation whether is any rule(defined in User model) breaked or not
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Check Whether user with same email id exist or not
try {
let user = await User.findOne({ email: req.body.email });
if (user) {
return res.status(400).json({ error: "Sorry user with same email id already exist" });
}
// hashing of password
const salt = await bcrypt.genSalt(10);
const securePassword = await bcrypt.hash(req.body.password, salt);
// create A new User
user = await User.create({
name: req.body.name,
email: req.body.email,
password: securePassword
})
// returning user id in Token
const JWT_secret = "Rishiisa#boy";
const data = { user:{id: user.id} };
const auth_token = jwt.sign(data, JWT_secret);
res.json({ auth_token });
}
catch (error) {
console.error(error.message);
res.status(500).send("Internal server error");
}
})
// Route:2 - Login a User using credential. "/api/auth/login". NO Login Required.
router.post('/login', [
body('email', 'Enter valid email').isEmail(),
body('password', 'password can not be blank').exists(),
], async (req, res) => {
// Check for vaidation according to the rule defined at line no. 53, 54;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// destructure the email and password from body request
const { email, password } = req.body;
try {
// Checking whether email is exist or not
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ error: "Please try to login using correct credentials" });
}
// Now Comparing password with help of bcryptjs
const comparepassword = await bcrypt.compare(password, user.password);
if (!comparepassword) {
return res.status(400).json({ error: "Please try to login using correct credentials" });
}
// Now if user enter coorect password and login then user got logged in;
// And We will send authtoken to user;
// returning user id in Token
const JWT_secret = "Rishiisa#boy";
const data = { user:{id: user.id} };
const auth_token = jwt.sign(data, JWT_secret);
res.json({ auth_token });
}
catch (error) {
console.error(error.message);
res.status(500).send("Internal server error");
}
})
// Route:3 - Get Loggedin User details using:POST "/api/auth/getuser" Login required
router.post('/getuser', fetchuser, async (req, res) => {
try {
const userid = req.user.id;
const user = await User.findById(userid).select("-password");
res.send(user);
} catch (error) {
console.error(error.message);
res.status(500).send("Internal server error");
}
})
module.exports = router
middleware:
fetchuser.js
const jwt = require('jsonwebtoken');
const JWT_secret = "Rishiisa#boy";
const fetchuser = (req, res, next) => {
// Get the user from jwt token and add user id to req object
const token = req.header('auth_token');
if (!token) {
res.status(401).send({ error: "Please authenticate using a valid token" });
}
try {
const data = jwt.verify(token, JWT_secret);
req.user = data.user;
next();
} catch (error) {
res.status(401).send({ error: "Please authenticate using a valid token" });
}
}
module.exports = fetchuser;
In auth.js, where you wrote: "const data = { user:{id: user.id} };" Try changing user.id to user._id, since in MongoDB the user id is referred to as '_id'.
Let me know if that works.
I've had problems sending jwt token back and even verifying it, but all is good on my side now.
Also, below is my (inspired) method of going about this:
router.post('/register', (req, res)=>{
const { username, password } = req.body;
const user = new User({
username,
password
});
bcrypt.genSalt(10, (err, salt)=>{
bcrypt.hash(user.password, salt, (err, hash)=>{
if(err) throw err;
user.password = hash;
user.save()
.then(user=>{
jwt.sign(
{ id: user._id },
process.env.jwtSecret,
{ expiresIn: 3600 },
(err, token) =>{
if(err) throw err;
res.status(200)
}
)
})
})
})
})
I am trying to make a PUT request with node.js using Javascript. Basically, what I am trying to do is make it so that an authenticated user is allowed to update a phone number and password. Normally I would have just used req.body in order to have the body be used to make an update request, however the whole body has a username, password and phoneNumber. I am only needing to update the password and phoneNumber. I have a restrict function that is restricting this request except for a logged in registered user, and I also have a model function for my update which is:
function updateUser(changes, id) {
return db("users")
.update(changes)
.where({id})
}
I also am trying to make sure that the password the user decided to update to (or the password they currently have) is hashed. I am using bcryptjs to hash the password. I have a two post request that both encrypts the password, (which is my register function) and one that compares the encryption (my login function). I will include those both just in case you need any background information:
router.post("/register", async (req, res, next) => {
try {
const {username, password, phoneNumber} = req.body
const user = await Users.findBy({username}).first()
if(user) {
return res.status(409).json({
message: "Username is already in use",
})
}
const newUser = await Users.create({
username,
password: await bcrypt.hash(password, 14),
phoneNumber,
})
res.status(201).json(newUser)
} catch (err) {
next(err)
}
})
router.post("/login", async(req, res, next) => {
try {
const {username, password} = req.body
const user = await Users.findBy({username}).first()
if(!user) {
return res.status(401).json({message: "Invalid Username or Password",})
}
const passwordValid = await bcrypt.compare(password, user.password)
if(!passwordValid) {
return res.status(401).json({message: "Invalid Username or Password",})
}
const token = jwt.sign({
userId: user.id,
}, process.env.JWT_SECRET)
res.cookie("token", token)
res.json({
message: `Welcome to your plant page ${user.username}!`
})
} catch (err) {
next(err)
}
});
When I was trying to start my PUT request, I had started off writing const {phoneNumber, password} = req.body but I am needing to use both phoneNumber and password in the function. Here is an example of what I was starting my code with:
router.put("/:id/updateaccount", restrict(), async(req, res, next) => {
try {
const {phoneNumber, password} = req.body
} catch(err) {
next(err)
}
})
I got it figured out after finding some help from someone in my class. I was on the right track with const {phoneNumber, password} = req.body. The rest is this (or this is all of the code):
router.put("/:id/updateaccount", restrict(), async(req, res, next) => {
try {
const {phoneNumber, password} = req.body
const userUpdate = await Users.updateUser({
phoneNumber, password: await bcrypt.hash(password, 14)
}, req.params.id)
res.status(200).json({
userUpdate:userUpdate, message: "You have successfully updated your information",
})
} catch(err) {
next(err)
}
})
I again used bcrypt to encrypt the newly updated password
I've been doing some small school project on making our own API and connecting it to an angular front end.
I've been following guide on things and I've came across the problem where my app started throwing internal server error 500 after implementing controllers.
It all worked fine until I've imported the controllers for user registration.
Posts controller works just fine, so does the login part of the ap
I tried logging the errors but it wouldnt output anything.
Here is my code:
user route
const express = require("express");
const UserController = require("../controllers/user");
const router = express.Router();
router.post("/signup", UserController.createUser);
router.post("/login", UserController.userLogin);
module.exports = router;
User controller
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const User = require("../models/user");
exports.createUser = (req, res, next) => {
bcrypt.hash(req.body.password, 10).then(hash => {
const user = new User({
email: req.body.email,
password: hash
});
user
.save()
.then(result => {
res.status(201).json({
message: "User created!",
result: result
});
})
.catch(err => {
res.status(500).json({
message: "Invalid authentication credentials!"
});
});
});
}
exports.userLogin = (req, res, next) => {
let fetchedUser;
User.findOne({ email: req.body.email })
.then(user => {
if (!user) {
return res.status(401).json({
message: "Auth failed"
});
}
fetchedUser = user;
return bcrypt.compare(req.body.password, user.password);
})
.then(result => {
if (!result) {
return res.status(401).json({
message: "Auth failed"
});
}
const token = jwt.sign(
{ email: fetchedUser.email, userId: fetchedUser._id },
"b9SNz3xg9gjY",
{ expiresIn: "1h" }
);
res.status(200).json({
token: token,
expiresIn: 3600,
userId: fetchedUser._id
});
})
.catch(err => {
return res.status(401).json({
message: "Invalid authentication credentials!"
});
});
}
I am expecting to be able to register an account which will be able to post new posts. It all worked just fine until I've made controllers and moved the requests and functions into controller file.
I really apologize for asking this probably simple question, but my programming skills are still low
In userLogin, when the user doesn't exist, you return res.status(401)..., which is chained to the next .then call as result (instead of your expectation that it would be the value returned by bcrypt.compare).
What you can do is instead of:
if (!user) {
return res.status(401).json({
message: "Auth failed"
});
}
try
if (!user) {
throw new Error("Auth failed");
}
which will be caught in your .catch.
I have to implement security on my app by preventing users to access other users profile.
route.js
router.get('/currentUser/:username', userAuth, (req, res) => {
User.findOne({
username: req.params.username
}).then(user => {
if (user) {
return res.status(200).json(user);
} else {
return res.status(404).json({
message: 'User not found'
});
}
});
});
and my userAuth.js
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
jwt.verify(token, 'app_secret_token');
next();
} catch (error) {
res.status(401).json({
message: 'Authentication failed!'
});
}
};
now if I am logged in as user test so my URL will be http://localhost:4200/currentuser/test but if I change my URL to another user test2 it redirects and loads the test2 even though I am logged as test
how do I prevent this?
You need to also check that the logged in user accesses his data.
you can achieve this by checking the user in the token against the requested page. This means you need to encode the user Id inside the jwt token. That will also make sure this parameter wasn't meddled with since jwt.verify would fail if someone tried to change the jwt token without having the secret.
you can add that data to the jwt token when signing it:
jwt.sign({
userId: 'username'
}, 'secret', { expiresIn: '1h' });
Basically if you save the same data as serializeUser\deserializeUser result, it should also work (the username is just a suggestion).
you can use the callback from jwt.verify to get the decoded token and retrieve that data
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
jwt.verify(token, 'app_secret_token', (err, decoded) => {
if (err) { throw err; }
const currentUsername = decoded.userId; // <-- this should be whatever data you encoded into the jwt token
// if the user requested is different than the user in the token,
// throw an authentication failure
if (req.originalUrl.includes('/currentUser/') &&
!req.originalUrl.includes(`/currentUser/${currentUsername}`)) {
throw new Error('access to other user data denied');
}
next();
});
} catch (error) {
res.status(401).json({
message: 'Authentication failed!'
});
}
};
Even though I think this might be a good case separating this into two different middlewares :-)
PS - as #anand-undavia mentioned, it might be better to identify the user request based on the jwt token itself instead of the 'url' itself. that way, each user should only have access to their own data and this problem can't occur at all.
basically, the user should be accessible with the method above (getting it from the token) or from a req.user field if you use any middleware that adds it automatically.
let us assume that user profile page id mydomian?passedId=userId so simply add profile-guard to check who can visit or activate this page, in CanActivate check if passed id id the same of current user id, then return true else redirect him to previous page
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
let passedId: string = next.queryParams.passedId;
let user = this.authService.GetUser();
if (user.id == passedId)
return true;
else {
this.router.navigate(['/home']); // or any page like un authorized to log to this page
return false;
}
}