req.user is undefined when using Passport-jwt on fetch user - javascript

I already checked multiple answers on Stackoverflow, and also went through on the documentation but I still cannot figure out my problem. when I try to sign in and signup it's working perfectly I have my token. It's nightmare to just fetch my current_user get('/isAuth') I get undefined !!!!
const Authentication = require("../controllers/authentication");
const passport = require("passport");
const requireAuth = passport.authenticate('jwt', {session: false});
const requireSignin = passport.authenticate('local', {session: false});
module.exports = app => {
app.post('/signup', Authentication.signup);
app.post('/signin', requireSignin, Authentication.signin);
// Current User is undefined !!!!!
app.get('/isAuth', Authentication.fetchUser);
my passport.js
const keys = require("../config/keys");
const passport = require("passport");
const User = require("../models/User");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const localStrategy = require("passport-local");
// Create local strategy
const localOptions = { usernameField: "email" };
const localLogin = new localStrategy(localOptions, function(email,password,done) {
// verify this username and password, call done with the user
// if it is the correct username and password
// otherwise, call done with false
User.findOne({ email: email }, function(err, user) {
if (err) {return done(err);}
if (!user) {return done(null, false);}
// compare passwords - is password is equal to user.password?
user.comparePassword(password, function(err, isMatch) {
if (err) {return done(err);}
if (!isMatch) {return done(null, false);}
return done(null, user);
});
});
});
// setup option for jwt Strategy
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'),
secretOrKey: keys.secret
};
// Create Jwt strategy
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
// See if the user Id in the payload exists in our database
// If does, call 'done' with that other
// otherwise, call done without a user object
User.findById(payload.sub, function(err, user) {
if (err) {return done(err, false);}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
// Tell passport to use this strategy
passport.use(jwtLogin);
passport.use(localLogin);
// Generate token
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
./controller/authentication.js
const User = require('../models/User');
const jwt = require('jwt-simple');
const config = require('../config/keys');
function tokenForUser(user){
const timestamp = new Date().getTime();
return jwt.encode({sub: user.id, iat: timestamp}, config.secret);
}
exports.signup = function(req,res,next){
console.log(req.body)
const email = req.body.email;
const password = req.body.password;
if(!email || !password){
return res.status(422).send({error: 'You must provide email and password'});
}
// See if user with the given email exists
User.findOne({email: email}, function(error, existingUser){
if (error){return next(error)};
// if a user with email does exist, return an error
if (existingUser){
return res.status(422).send({error: 'Email is in use'});
}
// if a user with email does not exist, create and save record
const user = new User({
email: email,
password: password
});
user.save(function(error){
if (error){return next(error);}
// respond to request indicating the user was created
res.json({token: tokenForUser(user)});
})
})
}
exports.signin = function (req,res,next){
// user has already had their email and password auth
// we just need to give them a token
res.send({token: tokenForUser(req.user)});
}
// here is my problem...
exports.fetchUser = function (req, res, next) {
console.log('this is ',req.user)
};
Still stuck for many days... it's a nightmare!!! if someone has the solution.
after sign in if I want to go to my route /isAuth to check my user data:

Have your tried using a middleware that calls the isAuthenticated function on req object? This function is added by passport and is generally the recommended way to check if a request is authenticated.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect("/");
}
Then you can call this function when a user hits your isAuth route:
app.get('/isAuth', isLoggedIn, Authentication.fetchUser);

Related

Post request error when attempting to log user in

I have a post route request function below to login a user. I keep getting 401 unauthorized errors when attempting to make the request. Based on my code below, is there any refactoring I can do to fix this? Many thanks!!!
router.post('/login', async (req, res, next) => {
try {
// attempt to find the user in database //
const user = await User.findOne({ username: req.body.username });
// if user entered doesn't match which is in the database throw an error //
if (!user) {
res.status(401).json('wrong credentials!')
var hashedPassword = Cryptojs.AES.decrypt(
user.password,
process.env.PASS_SEC);
var Orginalpassword = hashedPassword.toString(Cryptojs.enc.Utf8);
}
// check if password entered matches the orignal password entered during registration, if not return error //
else if ( Orginalpassword !== req.body.password ) {
res.status(401).json('wrong credentials!');
var accessToken = jwt.sign({
id: user._id,
isAdmin: user.isAdmin
},
process.env.JWT_SEC,
{expiresIn:'3d'}
);
var { password, ...others} = user._doc;
}
else {
// if password and username both match successfully log user in //
return res.status(200).json({...others, accessToken})
}
} catch (error) {
res.status(500).json(error);
}
});
here i did the simplest way to implement the login API try this👇🏻
router.post('/login', async (req, res, next) => {
try {
const {username, password} = req.body;
// attempt to find the user in database
const user = await User.findOne({ username });
// compares the password
if (user && await bcrypt.compare(password, user.password)) {
let accessToken = jwt.sign({
id: user._id,
isAdmin: user.isAdmin
},
process.env.JWT_SEC,
{expiresIn:'3d'}
);
user.accessToken = accessToken;
return res.status(200).json({...others, accessToken})
}
return res.status(401).send('wrong credentials!');
} catch (error) {
return res.status(500).json(error.message);
}
});

Cannot read properties of undefined (reading 'id') error JWT(Json Web Token)

Created MongoDb User Schema
MongoDb User
const mongoose = require("mongoose"); // Import mongoose
const UserSchema = new mongoose.Schema( // Create User Schema
{
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
isAdmin: {
type: Boolean,
default: false,
},
},
{ timestamps: true } // Add timestamps
);
module.exports = mongoose.model("User", UserSchema); // Export User Model
Used express.js Routes files in user.js
user.js
const User = require("../models/User"); // Import User Model Schema
const {
verifyTokenAndAuthorization
} = require("./verifyToken"); // Import verifyTokenAndAuthorization
const router = require("express").Router(); // Import express Router
//UPDATE
router.put("/:id", verifyTokenAndAuthorization, async (req, res) => {
if (req.body.password) {
req.body.password = CryptoJS.AES.encrypt( // Encrypt password
req.body.password, // Password
process.env.PASS_SEC // Encrypt password with secret key
).toString();
}
try {
const updatedUser = await User.findByIdAndUpdate( // Find user by id and update
req.params.id, // Find user by id
{
$set: req.body, // Set user data
},
{ new: true } // Return updated user
);
res.status(200).json(updatedUser); // Return user
} catch (err) { // Catch error
res.status(500).json(err); // Return error
}
});
module.exports = router;
Routes files in auth.js added access token
Access Token
const router = require('express').Router(); // import express
const User = require('../models/User'); // import user model
const CryptoJS = require('crypto-js'); // import crypto-js
const jwt = require('jsonwebtoken'); // import jsonwebtoken
//REGİSTER
router.post('/register', async (req, res) => {
const newUser = new User({
username: req.body.username, // username
email: req.body.email, // req.body.email is the same as req.body.email
password: CryptoJS.AES.encrypt(req.body.password,process.env.PAS_SEC).toString(), // encrypt password
}) // create new user
try{
const savedUser = await newUser.save(); // save user
res.status(201).json(savedUser); // send user
} catch(err){
res.status(500).json({message: err}); // send error
}
});
//LOGIN
router.post("/login", async (req, res) => {
try{
const user = await User.findOne({username: req.body.username}); // find user
if(!user){ // if user is not found
return res.status(400).json({message: "User not found"}); // send error
}
const hashedPassword = CryptoJS.AES.decrypt(user.password,process.env.PAS_SEC); // decrypt password
const Originalpassword = hashedPassword.toString(CryptoJS.enc.Utf8); // convert to string
const {password,...others} = user._doc; // others is the user data without password // Güvenlik için password olmadan kullanıcıyı aktardık ve ._doc ise mongodb datamızı document içinden gösteriyor
const accessToken = jwt.sign({ // create access token
userId: user._id, // user id
isAdmin: user.isAdmin // isAdmin
},
process.env.JWT_SEC, // secret key
{expiresIn: "24h"} // expire time
);
if(Originalpassword !== req.body.password){ // if password is not correct
return res.status(401).json({message: "Incorrect password"}); // send error
} else{
res.status(200).json({...others,accessToken}); // send success
}
}catch(err){ // if error
res.status(500).json({message: err}); // send error
}
});
module.exports = router; // export router
Routes files in verifyToken.js
VerifyToken.js
const jwt = require("jsonwebtoken"); // Import jsonwebtoken
const verifyToken = (req, res, next) => { // Verify token
const authHeader = req.headers.token; // Get token from header
if (authHeader) { // If token exists
const token = authHeader.split(" ")[1]; // Get token from header
jwt.verify(token, process.env.JWT_SEC, (err, user) => { // Verify token
if (err) res.status(403).json("Token is not valid!"); // Return error
req.user = user; // Set user
next(); // Call next middleware
});
} else {
return res.status(401).json("You are not authenticated!"); // Return error
}
};
const verifyTokenAndAuthorization = (req, res, next) => { // Verify token and authorization
verifyToken(req, res, () => {
if (req.user.id === req.params.id || req.user.isAdmin) { // If user id is equal to id from url or user is admin
next(); // Call next middleware
} else {
res.status(403).json("You are not alowed to do that!"); // Return error
}
});
};
module.exports = {
verifyToken, // Export verifyToken
verifyTokenAndAuthorization, // Export verifyTokenAndAuthorization
};
I used Postman. All the queries are working, but when I want to update the user, postman also gives an error. How can ı fix this error is : Cannot read properties of undefined (reading 'id') error. i cant see where i went wrong pls hep mee
Postman Error is
You have to send the JWT token that you get in the response of your login call to the request to update the user.

Cannot read properties of undefined (reading 'id')

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)
}
)
})
})
})
})

Error: Unknown authentication strategy "local" while trying to signin

I have been building this project from a tutorial. The signup functionality works fine but the login feature doesn't work. Whenever I try logging in a registered user using postman the error I get is
Error: Unknown authentication strategy "local"
In the other posts on stack overflow, I didn't find a solution to this error. Passport, passport-local and passport-jwt are all installed so that shouldn't be the issue. I would really appreciate any sort of help.
passport.js
require('dotenv').config();
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const JWTStrategy = require('passport-jwt').Strategy;
const User = require('./models/User');
// Environment variables
const STRATEGY_KEY = process.env.STRATEGY_KEY;
const cookieExtractor = req => {
let token = null;
// Retrieve the token from cookies
if (req && req.cookies) {
token = req.cookies['access_token'];
}
return token;
};
const jwtOptions = {
jwtFromRequest: cookieExtractor,
secretOrKey: STRATEGY_KEY,
};
// Authorization for protected routes
passport.use(
new JWTStrategy(jwtOptions, (payload, done) => {
User.findById({ _id: payload.sub }, (err, user) => {
// Check for error
if (err) return done(err, false);
// Check if user exists
if (user) return done(null, user);
return done(null, false);
});
})
);
// Local strategy using username and password
passport.use(
new LocalStrategy((username, password, done) => {
User.findOne({ username }, (err, user) => {
// Error while fetching the user from database
if (err) return done(err);
// No such user exists
if (!user) return done(null, false);
// Check if entered password matches
user.comparePassword(password, done);
});
})
);
routes.js
require('dotenv').config();
const express = require('express');
const passport = require('passport');
const router = express.Router();
const STRATEGY_KEY = process.env.STRATEGY_KEY;
const signToken = userID => {
return jwt.sign(
{
iss: STRATEGY_KEY,
sub: userID,
},
STRATEGY_KEY,
{
expiresIn: '1h',
}
);
};
router.post(
'/signin',
passport.authenticate('local', { session: false }),
(req, res) => {
if (req.isAuthenticated()) {
const { _id, username, email } = req.user;
const token = signToken(_id);
res.cookie('access_token', token, {
httpOnly: true,
sameSite: true,
});
res.status(200).json({
isAuthenticated: true,
user: {
username,
email,
},
});
}
}
);
module.exports = router;
So after many hours of debugging, the solution I found to this problem was that I didn't import passport.js file in routes.js file, which I was not expecting since that import stays there ideal not doing anything, not being part of any code(exceot the import) but I was wrong. The passport configuration we make in that file is imported under the hood even though it doesn't take part in any further lines of that file.

bcrypt error with passport and mongoose

Bcrypt is throwing an Incorrect arguments error which I traced back to this function in user.js
userSchema.methods.comparePassword = (candidatePassword, callback) => {
bcrypt.compare(candidatePassword, this, (err, isMatch) => {
console.log('candidatePassword= ', candidatePassword, '& this= ', this);
if (err) { return callback(err); }
callback(null, isMatch);
});
};
/*
candidatePassword= bird
this= {}
this.password= undefined */
The user object is coming back as an empty object, and therefore this.password is undefined. I assume the this parameter in bcrypt.compare refers to the userSchema instance. The userSchema is declared in passport.js
const passport = require('passport');
const ExtractJwt = require('passport-jwt').ExtractJwt;
const JwtStrategy = require('passport-jwt').Strategy;
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user');
const config = require('../config');
var localOptions = {
usernameField: 'email',
};
// Verifies user by checking if a password matches the specified email during signin
var localStrategy = new LocalStrategy(localOptions, function (email, password, done) {
User.findOne({ email:email.toLowerCase()}, function (err, user) {
console.log('/passport.js/localStrategy- user object: ', user)
if (err) { return done(err); }
if (!user) { return done(null, false); }
user.comparePassword(password, function (err, isMatch) {
console.log('/passport.js/localStrategy- password: ', password)
if (err) { return done(err); }
if (!isMatch) { return done(err, false); }
return done(null, user);
});
});
});
// ... jwt strategy ...
passport.use(localStrategy);
/*
user object: { _id: 58a1018dc3f89eb5955b8638,
email: 'bird#bird.com',
password: '$2a$10$lAJ9hoGKt9ggfk1TISfkOedxDIs/waLB5e4PccHAKt286XCKCY0/q',
__v: 0 } */
I'm not sure quite what the issue as it seems a user object is returned with an encrypted password field from mongodb, and user.comparepassword() is called...
I signed the user up with the same Schema object as well.
Any help / tips appreciated!
You are only setting up your model so that it pulls in the candidatePassword but never finds the stored password from the database. Since this is returning an empty object, either the email is not being matched or the password is not being compared to what is stored. Try simplifying the comparePassword function and adding 'sync' to the bcrypt.compare which removes the need for a callback.
In models:
userSchema.methods.comparePassword = (candidatePassword) => {
return bcrypt.compareSync(candidatePassword, this.password);
};

Categories

Resources