I'm creating social network app with MERN. I have implemented followers and following, and now i'm trying to get the list of posts of only users that i'm following. So, if I console.log my req.user I only get the 'id' and 'iat', but i need more information of a user such as following array.
here is what i have:
auth.js middlware:
const config = require('config')
const jwt = require('jsonwebtoken')
function auth(req, res, next) {
const token = req.header('x-auth-token')
// check for token
if (!token) return res.status(401).json({ msg: 'Unauthorized token' })
try {
// verify token
const decoded = jwt.verify(token, config.get('jwtSecret'))
// add user from payload
req.user = decoded
console.log('---',decoded)
next()
} catch (e) {
return res.status(400).json({ msg: 'Token is not valid' })
}
}
module.exports = auth
auth.js
const express = require('express')
const router = express.Router()
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const config = require('config')
const User = require('../models/User')
const auth = require('../middleware/auth')
// #route POST /api/auth
// #desc Login user
// #access Public
router.post('/', (req, res) => {
const { email, password } = req.body
// filed validation
if (!email || !password) res.status(400).json({ msg: 'Fields cannot be empty.' })
// Check if user is registered
User
.findOne({ email })
.then(user => {
if (!user) res.status(400).json({ msg: `User doesn't exist.` })
//console.log(user)
// validate password
bcrypt
.compare(password, user.password)
.then(isMatch => {
if (!isMatch) return res.status(400).json({ msg: 'Invalid password' })
// if password matches, send token and user
jwt.sign(
{ id: user.id },
config.get('jwtSecret'),
(err, token) => {
if (err) throw err
res.json({
token,
user: {
id: user.id,
email: user.email,
first_name: user.first_name,
last_name: user.last_name,
registration_date: user.registration_date,
profile_image: user.profile_image,
user_bio: user. user_bio,
followers: user.followers,
following: user.following
}
})
}
)
})
})
})
and this is my console log
Server started at port 5000
[0] --- { id: '5efccfb13224e1489439bcfd', iat: 1593626545 }
[0] * { id: '5efccfb13224e1489439bcfd', iat: 1593626545 }
EDIT:
// #route GET /api/posts/subscribedPost
// #desc get all subscribed post
// #access Private
router.get('/subscribedPost', auth, (req, res) => {
console.log('*',req.user) // not working correctly, have only id and iat
Post
.find({userID: req.user.id})
.populate('userID', 'first_name last_name profile_image _id')
.sort({ registration_date: -1 })
.then(post => res.json(post))
.catch(err => res.json(err))
})
This is because you are signing your JWT token with id only, so when u decode it you will only get id
jwt.sign({ id: user.id },config.get('jwtSecret'),(err, token) =>{})
Here you are just passing id, you can pass your whole user object to get all the data
jwt.sign(user.toJSON(),config.get('jwtSecret'),(err, token) =>{})
PS- YOU MIGHT NOT WANT TO PASS YOUR WHOLE USER, JUST DID IT FOR DEMO
You can customize to what values you want.
You need to get user data DB in auth middleware and then add it to req.user. So if there are any changes to the user you get updated user data.
eg:
function auth(req, res, next) {
const token = req.header('x-auth-token')
// check for token
if (!token) return res.status(401).json({ msg: 'Unauthorized token' })
try {
// verify token
const decoded = jwt.verify(token, config.get('jwtSecret'))
// get the user from DB
const user = User.findById(decoded.id) //only populate fields which are required
req.user = user
console.log('---',decoded)
next()
} catch (e) {
return res.status(400).json({ msg: 'Token is not valid' })
}
}
Related
This is my app.post()which gets form data from client.
app.post('/api/login', async (req, res) => {
const { emailid, password } = req.body
const user = await User.findOne({ emailid }).lean()
if (!user) {
return res.json({ status: 'error', error: " Invalid username/Password" })
}
if (bcrypt.compare(password, user.password)) {
const token = jwt.sign({ id: user._id, emailid: user.emailid }, 'secret', { expiresIn: '24h' })
return res.json({ status: 'ok', data: token, user_id: user._id })
}
res.json({ status: 'error', error: " Invalid username/Password" })
})
I need to pass the jwt token or the user_id to my
app.get('/', (req,res)=>{
res.render('index')
})
For this, you will need to create an authentication middleware that will check your request headers for a jwt token, which you can then decode to get the user_id or any other data that you passed to it during encryption. A sample middleware can look like the one below
const isAuth = (req) => {
const authorization = req.headers["authorization"];
if (!authorization) throw new Error("You need to log in");
const token = authorization.split(" ")[1];
const { user_id} = verify(token, process.env.ACCESS_TOKEN_SECRET);
return {user_id, token};
};
After you setup your authorization middleware, you can then go ahead and use it in your routes like so
app.get('/', (req,res)=>{
const {token, user_id) = isAuth(req);
//use token and user_id here or throw error if not available in case this is a protected route
res.render('index')
})
I have this code for login. How do I use the current user's information from this code to another file using postman and node.js?
exports.loginUser = (req,res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err ,result) => {
if(err){
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY ,
{
//options
expiresIn: "1h"
});
You should tell exactly what you want, what you said is confusing, but If you mean how to pass the logged in user to the next middleware, you gotto assign the user to req
exports.loginUser = async (req, res, next) => {
const user = await User.find({ email: req.body.email }).exec()
if (user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY, {
//options
expiresIn: "1h"
});
req.user = user[0];
return next();
}
})
}
Then in the next middleware you have access to logged in user, using req.user.
UPDATE:
To implement the functionality that you want, according to what you described in the comment:
Before anything import these packages:
const jwt = require("jsonwebtoken");
const { promisify } = require("util");
First you implement a route that checks for credentials and sends back a signed jwt:
exports.login = CatchAsync(async(req, res, next) => {
const { email, password } = req.body;
if (!email || !password) {
return next(new Error("Please provide email and password"));
}
const user = await UserModel.findOne({email});
if (!user) {
return next(new Error("There is no user with that email"));
}
if(!(await bcrypt.compare(password, user.password))) {
// actually the pass is not correct but for security reasons we don't say that
return next(new Error("Email or password is not correct");
}
// pass the user id to jwt so later can identify user
const token = jwt.sign({ id: user._id }, 'yourJwtSecret', {
expiresIn: '90d',
});
// httpOnly prevents access to token in client's browser, so it is safe
const cookieOptions = {
expires: new Date(
Date.now() + 90 * 24 * 60 * 60 * 1000
),
httpOnly: true,
};
res.cookie("jwt", token, cookieOptions);
res.status(200).json({
status: 'success',
message: 'logged in successfully'
});
});
Then for every route that needs to check for logged In user, use this middleware:
exports.isLoggedIn = CatchAsync(async(req, res, next) => {
// Check if there is a token
// if no token was provided it means user is not logged in
let token;
if (req.cookies.jwt) {
token = req.cookies.jwt;
} else {
return next();
}
// Verify token
// decoded now has access to id of user
let decoded;
try {
decoded = await promisify(jwt.verify)(token, 'yourJwtSecret');
} catch (err) {
// if token was modified or expired or not valid
return next();
}
// get the user
const user = await UserModel.findOne({
_id: decoded.id
});
// access granted, user is logged in
req.user = user; // you can access the logged in user in the next middleware
res.locals.user = user; // you can access the logged in user in template engines
next();
});
If the user is not logged in, req.user won't be assigned. therefore in next middlewares if req.user was undefined you know user is not logged in.
for more info jwt docs.
If you have never taken any NodeJs course, I'd recommend this course
In my project, when a user logs in, I generate an auth token like so:
// For my User model
userSchema.methods.generateAuthToken = () => {
const token = jwt.sign({ _id: this._id }, config.get('jwtPrivateKey'))
return token
}
I then apply an auth middleware on a route that allows a user to create posts.
auth middleware:
module.exports = (req, res, next) => {
const token = req.header('x-auth-token')
if (!token) return res.status(401).send('No token provided')
try {
const decoded = jwt.verify(token, config.get('jwtPrivateKey'))
req.user = decoded
next()
}
catch (ex) {
res.status(400).send('Invalid token')
}
}
My route:
router.post('/', auth, upload.single('postImage'), async (req, res) => {
console.log(req.user._id)
const post = new Post({
postImage: `http://localhost:3000/${req.file.path}`,
caption: req.body.caption,
user: req.user._id
})
await post.save()
res.send(post)
})
As you can see, I log the req.user._id in the route, and when I call this endpoint, I get undefined logged to the console. Instead I expect the _id of the user who generated the auth token.
Below is my login route, where a user gets their auth token.
Login route:
router.post('/', validateLogin, async (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) return res.status(422).send(errors.array())
let user = await User.findOne({ $or: [{ username: req.body.usernameEmail }, { email: req.body.usernameEmail }]})
if (!user) return res.status(400).send('Invalid username/email or password')
const validPassword = await bcrypt.compare(req.body.password, user.password)
if (!validPassword) return res.status(400).send('Invalid username/email or password')
const token = user.generateAuthToken()
res.send(token)
})
Does anybody know the issue here? Thanks.
I am new in backend Node.js and till now I am able to complete registration and login with authentication.
When login I am getting token in response by using jwt token
Now I want to have the registration details to be shown to users after login. After login the details must of be of particular user's only whos is logging in.
And if admin is logging in, then he will get the entire database user's fields.
This is my index.route:-
const express = require ('express');
const router = express.Router();
const mongoose = require ('mongoose');
const User = mongoose.model('User');
const ctrlUser = require ('../controllers/user.controller.js');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
// routing functions
//registartion user signup
router.post('/register' , ctrlUser.register);
//login user
router.post('/login' , (req, res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: "Auth failed. User not found."
})
}
bcrypt.compare(req.body.password, user[0].password, (err, result) =>{
if (err) {
return res.status(401).json({
message: "Auth failed. Check email and password"
});
}
if (result){
const adminEmail = "rohit#metapercept.com";
const role = user[0].email===adminEmail? "admin" : "user"; //check user id as admin or user
const token = jwt.sign(
{
email: user[0].email,
userId: user[0]._id,
role
},
process.env.JWT_KEY,
{
expiresIn : "1h"
});
return res.status(200).json({
message: "Auth Successful",
token : token
});
res.redirect('/profile');
}
});
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Something went wrong in login"]);
else
return next(err);
});
});
router.get('/profile', function(req, res, next){
//something todo here ...
});
//delete user
router.delete('/:userId' , (req, res, next) =>{
User.deleteMany({_id: req.params.userId})
.exec()
.then(result => {
res.status(200).send(["Deleted"]);
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Didn't get deleted"]);
else
return next(err);
});
});
module.exports = router;
How can I access user's details in profile url API?
Get JWT from request header then decode
jwt.verify(token, getKey, options, function(err, decoded) {
console.log(decoded.email)
});
jwt.verify - jwt doc
Create new middleware ( above other routes)
// route middleware to verify a token
router.use(function(req, res, next) {
// 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.'
});
}
});
Help : jwt - decode and save in req
In the code, After return your redirect never work. so There're 2 options:
You don't need to return a token to client, just use res.redirect('/profile') after your verification. (in this way, your server and client are in one)
You just return the token to client (Don't use res.redirect('/profile') anymore) then client will use that token to redirect to the profile. (in this way, your server and client are separate each other).
I am trying to make get request to my protected routes using bearer token and it returns unautorized, even after sending token through header.
I am using bearer token on nodejs, expressjs app using mlab remote database
I registered new user, then I logged with that email and it sent me back a token(as expected).
When I sent this token through header of other route without login it returns Unautorized.
my steps are
1) registered with new email
2) login request successful
3) failed get request to route localhost:5000/api/users/current, and returns Unautorized.
user.js file has
// users.js for authentication and authorization
const express = require("express");
const router = express.Router();
const gravatar = require("gravatar");
const bcrypt = require("bcryptjs");
const keys = require("../../config/keys");
const jwt = require("jsonwebtoken");
const passport = require("passport");
// Load User Model to check existing email is used for registration or not?
const User = require("../../models/User");
// #route GET request to api/users/test
// #description Tests users route
// #access Public, without login
router.get("/test", (req, res) => res.json({ msg: "Users Works" }));
// #route GET request to api/users/register
// #description new registration of user.
// #access Public, without login first register
router.post("/register", (req, res) => {
User.findOne({ email: req.body.email }).then(user => {
if (user) {
return res.status(400).json({ email: "Email value exists already." });
} else {
console.log("no user found of this email in DB");
const avatar = gravatar.url(req.body.email, {
s: "200", //Size of gravatar in pixels
r: "pg", //rating,
d: "mm" //default value= 'mm'
});
// create user
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar,
password: req.body.password
});
// gensalt(noOfSalts_of_Iterations,(err,salt_result)=>{})
bcrypt.genSalt(10, (err, salt) => {
// hash(plaintext,salt,callback(err,resultant ciphertext))
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) {
console.log("error in bcrypt.hash()");
throw err;
}
//assign salted hash to password
newUser.password = hash;
// Save new password in datebase, overriding plaintext;
newUser
.save()
.then(user => res.json(user)) // if yes,then send it as argument in brackets.
.catch(err =>
console.log("Error occured in saving hash password in DB\n")
);
});
});
}
});
});
// #route GET request to api/users/login
// #description Login/signing-in registered user. return JWT token
// #access Public
router.post("/login", (req, res) => {
const email = req.body.email;
const password = req.body.password;
// find user to match it's password
User.findOne({ email: req.body.email }).then(user => {
//check if no user
if (!user) {
return res.status(404).json({ email: "User's email found." });
}
// else if do this..
// if user's email-id is found then match it's password-hash with local-database
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// user pswd matched => then return JWT token back for authentication
// res.json({ msg: "Success" });
const payload = { it: user.id, name: user.name, avatar: user.avatar };
// created JWT token
// now sign token
// jwt.sign(payload, secretKey, expire-time, callback );
// jwt.sign
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 },
(err, token) => {
res.json({
success: true,
token: "bearer " + token
});
}
);
} else {
// pswd doesn't matched
return res.status(400).json({ password: "Password didn't match" });
}
});
});
});
// #route GET request to api/users/current - current user with token
// #description Return current user
// #access Private, can't go without login
router.get(
"/current",
passport.authenticate("jwt", { session: false }),
(req, res) => {
res.json({ msg: "Success" });
}
);
module.exports = router;
data is stored succesfully at remote db mlab, but I can't figureout what's problem.
my Github repo of this project is this
Please Look at the Line of your code Users.js Line 88 the payload id is stored in 'it'
const payload = { it: user.id, name: user.name, avatar: user.avatar };
But in your passport.js
User.findById(jwt_payload.id)
you are extracting it by id so if you use here jwt_payload.it the you will get message success