passport JWT 'Unauthorized' Node - node.js

I'm making authorizing with token and when I test with postman, I keep getting 'Unauthorized' after logged in and go to profile
Login code:
router.post('/authentication', (request, response, next) => {
const email = request.body.email;
const password = request.body.password;
userModel.getUserByEmail(email, (err, user) => {
if (err) throw err;
else {
if (!user) {
console.log('User does not exists!');
response.json({ success: false, msg: 'User does not exists!' });
} else {
userModel.comparePassword(password, user.password, (err, isMatch) => {
if (err) throw err;
else {
if (!isMatch) {
response.json({ success: false, msg: 'Password do not match!' });
} else {
const token = jwt.sign(user, config.secret, {
expiresIn: 86400 // 1 day
});
response.json({
success: true,
token: "JWT " + token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
}
}
});
}
}
});
});
passport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const userModel = require('../models/usersModel');
const config = require('../config/database');
module.exports = function (passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
userModel.getUserById(jwt_payload._doc._id, (err, user) => {
console.log(jwt_payload);
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
tested route :
router.get('/profile', passport.authenticate('jwt', { session: false }), (request, response, next) => {
response.json({ user: request.user });
});
(PS: I tried console.log(jwt_payload); and shows nothing in console. Still stucking in this please help. I'm a starter. )

After you get your jwt token, you should send it in every request.
header: authorization: JWT "token"
Are you doing it?

Related

How to handle multiple session when using multiple stratgies in PassportJs with ExpressJs?

I'm developping an app where I want the user to login with thier discord account (Need do some check; checking user guilds, and roles) then when the user met the requirements, then I can allow that user authenticate with twitter to get some permissions. The first created session(session.sid) is overided by the last strategy used.
I'm using Mongodb to store users sessions, twitter, and discord information. I want to keep the previous sessions for other provider.
Routes part:
router.get('/discord', passport.authenticate('discord'), (req, res) => {
res.send(200)
})
router.get('/discord/redirect', passport.authenticate('discord'), (req, res) => {
res.send({msg: "Success loging with discord..."})
})
router.get('/discord-status', (req, res) => {
return req.user ? res.send(req.user):res.send({error: 403, message: 'Unauthorized user.'
})
})
router.get('/twitter', passport.authenticate('twitter', { failureRedirect: '/login', failureMessage: true }),
function(req, res) {
res.redirect('/');
})
router.get('/twitter/redirect', passport.authenticate('twitter'), (req, res) => {
res.send({msg: "Success loging with twitter..."})
})
router.get('/twitter-status', (req, res) => {
console.log(req.user)
return req.user ? res.send(req.user):res.send({error: 403, message: 'Unauthorized user.'
})
})
Discord passport part:
import passport from 'passport';
import { Profile, Strategy } from 'passport-discord';
import { VerifyCallback } from 'passport-oauth2';
import { User } from '../database/schemas';
import { config } from "dotenv"
// get env variables
config()
passport.serializeUser((user: any, done) => {
console.log(`Serialization: ${user} - ${user.id}`)
return done(null, user.id);
});
passport.deserializeUser(async (id: string, done) => {
console.log(`Deserialization: ${id}`)
try {
const user = await User.findById(id);
return user ? done(null, user) : done(null, null);
} catch (err) {
console.log(err);
return done(err, null);
}
});
passport.use(
new Strategy(
{
clientID: process.env.DISCORD_CLIENT_ID!,
clientSecret: process.env.DISCORD_CLIENT_SECRET!,
callbackURL: process.env.DISCORD_CALLBACK_URL,
scope: ['identify', 'email', 'guilds'],
},
async (
accessToken: string,
refreshToken: string,
profile: Profile,
done: VerifyCallback
) => {
const { id: discordId } = profile;
try {
const existingUser = await User.findOneAndUpdate(
{ discordId },
{ accessToken, refreshToken },
{ new: true }
);
if (existingUser) return done(null, existingUser);
const newUser = new User({ discordId, accessToken, refreshToken });
const savedUser = await newUser.save();
return done(null, savedUser)
// TO NOT AUTHORIZE THE USER WHEN ISN'T IN GUILD
// AND HASN'T THE REQUIRED ROLE
// return done(null, false)
} catch (err) {
console.log(err);
return done(err as any, undefined);
}
}
)
);
Twitter passport part:
const passport = require('passport')
import {config} from "dotenv"
var Strategy = require('passport-twitter');
import { TwitterAccounts } from '../database/schemas';
// get env variables
config()
passport.serializeUser((user: any, done: any) => {
console.log(`Serialization: ${user} - ${user.id}`)
return done(null, user.id);
});
passport.deserializeUser(async (id: string, done: any) => {
console.log(`Deserialization: ${id}`)
try {
const user = await TwitterAccounts.findById(id);
return user ? done(null, user) : done(null, null);
} catch (err) {
console.log(err);
return done(err, null);
}
});
passport.use(
new Strategy(
{
consumerKey: process.env.TWITTER_CONSUMER_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_SECRET_KEY!,
callbackURL: process.env.TWITTER_CALLBACK_URL,
},
async (
token: any,
tokenSecret: any,
profile: any,
cb: any
) => {
const { id: twitterId, username: userName } = profile;
try {
const existingUser = await TwitterAccounts.findOneAndUpdate(
{ twitterId },
{ token, tokenSecret, userName},
{ new: true }
);
console.log({ twitterId }, { token, tokenSecret, userName})
if (existingUser) return cb(null, existingUser);
const newUser = new TwitterAccounts({ twitterId, token, tokenSecret, userName });
const savedUser = await newUser.save();
return cb(null, savedUser)
} catch (err) {
console.log(err);
return cb(err as any, undefined);
}
}
)
);
The discord part is working and the session persist:
Session {
cookie: {
path: '/',
_expires: 2023-10-25T15:47:55.818Z,
originalMaxAge: 31104000000,
httpOnly: true
},
passport: { user: '635e9cab54916ceacbb2035c' }
}
but for the twitter it isn't
Session {
cookie: {
path: '/',
_expires: 2023-10-25T15:38:35.503Z,
originalMaxAge: 31104000000,
httpOnly: true
},
passport: {}
}
As you see the passport key is empty.
How to handel multiple cookie please, any help or advice will be appreciated. Thanks

Passport that authenticating user

I have the following code
passport.authenticate("local", (err, token, user) => {
if (err) {
console.dir('test4');
console.dir(err);
return res.json({
status: "error",
error: "Invalid user.",
});
}
console.dir(token);
if (!user) {
console.dir('test5');
return res.json({
status: "error",
error: "Invalid user.",
});
} else {
req.logIn(user, (err) => {
if (err) {
console.dir('test6');
console.dir(err);
return res.json({
status: "error",
error: "Invalid user.",
});
}
res.clearCookie("auth");
res.cookie("auth", token);
req.session.user = result;
return res.json({
status: "ok",
result: {
token,
user
}
});
});
}
})(req, res, next);
})
.catch((err) => {
console.dir(err);
return res.json({
status: "error",
error: _.isObject(err) ? "There was an error processing your request. Please try again later." : err,
});
});
I'm not able to authenticate the user using passport. Here's my passportConfig
const User = require("./models/user");
const LocalStrategy = require("passport-local").Strategy;
const jwt = require('jsonwebtoken');
module.exports = function (passport) {
passport.use(
new LocalStrategy((username, password, done) => {
User.login(username, password).then(result => {
const payload = {
sub: result[0].id
};
// create a token string
const token = jwt.sign(payload, process.env.JWT_SECRET);
return done(null, token, result[0]);
}).catch(err => {
return done(null, false);
});
})
);
passport.serializeUser((user, cb) => {
cb(null, user.id);
});
passport.deserializeUser((id, cb) => {
console.dir('test33');
console.dir(id);
User.fetchByEmail(id).then(result => {
cb(null, result[0]);
}).catch(err => {
cb(err, null);
});
});
};
and here's my index.js
app.use (
session ({
secret: "x",
resave: false,
saveUninitialized: true,
// proxy: true,
// rolling: true,
cookie: {
expires: new Date(Date.now() + (60*60*1000*24*90)),
secure: true
}
})
);
app.use(cookieParser());
app.use(passport.initialize());
app.use(passport.session());
require("./passportConfig")(passport);
what I'm I doing wrong?
The code brings out "test5".

JSON Web TOken Strategy

This is passport function to extract headers. I am using fromAuthHeaderWithScheme one, I already tried fromAuthHeaderAsBearerToken with bearer token as well. I could not make it work no matter what?
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
User = require('../models/user'),
Config = require('../config/database');
module.exports = function(passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme("JWT");
opts.secretOrKey = Config.secret;
//Code only comes until here.
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
console.log(jwt_payload);//Code never reaches here.
User.getByUserId({
id: jwt_payload._id
}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
Next is my getUserById function
module.exports.getByUserId = function(id, cb) {
User.findById(id, cb)
}
Next, is where above two gets called:
router.post('/login', function(req, res) {
let username = req.body.username;
password = req.body.password;
User.getByUserName(username, function(err, user) {
if (err) {
throw err;
}
if (!user) {
return res.json({
success: "false",
msg: "User not found"
})
}
//if found compareUser to regiestred one
User.comparePassword(password, user.password, function(err, isMatched) {
if (err) {
throw err;
}
if (isMatched) {
const token = jwt.sign(user.toJSON(), CONFIG.secret, {
expiresIn: 3600 /*Logout in 1 hour*/
});
res.json({
success: "true",
token: 'JWT ' + token,
user: user._id,
email: user.email,
username: user.username,
});
} else {
return res.json({
success: "false",
msg: " Password not Matched"
});
}
});
});
});
And these are comparePassword and getUserByName incase you need to see:
module.exports.comparePassword = function(typedPassword, hash, cb) {
bcrypt.compare(typedPassword, hash, (err, isMatched) => {
if (err) {
throw err;
}
return cb(null, isMatched);
})
};
module.exports.getByUserName = function(username, cb) {
const query = {
username: username
}
User.findOne(query, cb);
}
The secret key is same every where, that is not the issue. I cannot seem to figure out the issue.
router.get("/profile", passport.authenticate('jwt', {
session: false
}, function(req, res, next) {
res.json({
success: true,
message: "This is user profile",
user: req.user
});
}));
Now, above is how I authenticate, using postman and sending request as content type "Authorization" and The token. Encase, any of you are wondering, I already tried 'bearer '+token through bearer scheme.
I changed the first code block I posted above to this
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
keys = require('./keys'),
mongoose = require('mongoose'),
User = require('../models/User');
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken('Bearer');
opts.secretOrKey = keys.secretOrKey;
module.exports = passport => {
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.sub }, (err, user) => {
User.findById(jwt_payload.id)
.then(user => {
if (user) {
return done(null, user);
}
return done(null, false);
})
.catch(err => console.log(err));
});
}))
};
And second block to this. Basically change the token from 'JWT' to 'Bearer'.
router.post('/login', (req, res) => {
const email = req.body.email, password = req.body.password;
User.findOne({ email: email })
.then(user => {
if (!user) {
res.status(404).json({ msg: 'User not found' })
}
//Check password
bcrypt.compare(password, user.password)
.then(isMatch => {
if (isMatch) {
//User found
//Create Jwt Payload
const payload = {
id: user.id,
name: user.name,
avatar: user.avatar
}
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 },
(err, token) => {
res.json({
success: true,
token: 'Bearer ' + token
});
});
} else {
return res.status(400).json({ password: 'Password do not match' })
}
})
// .catch(err => console.log(err));
})
});
Now its working for me.

NodeJs JWT Passport

I use nodejs with passport Auth JWT. I can create the JWT Token but If I secure my route with passport.authenticate('jwt') it's not work and i have an errors.
My Error :
TypeError: Cannot read property '_id' of undefined
at JwtStrategy._verify (D:\Programes\nodejs\node\CRUD_NodeAngular5\NodeServer\config\passport.js:15:39)
at D:\Programes\nodejs\node\CRUD_NodeAngular5\NodeServer\node_modules\passport-jwt\lib\strategy.js:110:26
at D:\Programes\nodejs\node\CRUD_NodeAngular5\NodeServer\node_modules\passport-jwt\node_modules\jsonwebtoken\verify.js:27:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
Passport.js
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/User');
var config = require('./database');
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload.$__._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Login
router.post('/login', function(req, res) {
User.findOne({ email: req.body.email }, function(err, user) {
if (err) throw err;
if (!user) {
res.send({ success: false, message: 'Authentication failed. User not found.' });
console.log('User not found');
} else {
user.comparePassword(req.body.password, function(err, isMatch) {
if (isMatch && !err) {
var token = jwt.sign(user.toJSON(), config.secret, {expiresIn: 1000 });
var decoded = jwt.decode(token);
console.log(decoded);
res.json({ success: true, token:'JWT ' + token, decoded: decoded });
console.log('Connected : ' + token);
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
console.log('Password is wrong');
}
});
}
});
});
Route Dashboard Not work
router.get('/dashboard', passport.authenticate('jwt', { session: false }), function(req, res) {
res.send('It worked! User id is: ' );
});
lets clean your code it's simple :-
Step1: create config file and load where you keep your routes
var checkAuth = require('../config/check-auth');
Step2: copy paste it in check-auth
const jwt = require('jsonwebtoken');
module.exports = (req,res,next)=>{
try{
const token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token,"secret");
req.userData = decoded;
next();
}catch(error){
return res.status(401).json({message:'Auth failed'});
}
}
step 3 :protect your route
router.get('/dashboard',checkAuth , { session: false }),
function(req, res) {
res.send('It worked! User id is: ' );
});
protect all your routes by passing checkAuth as parameter

Node.js passport-jwt how to send token in a cookie?

1) Once user is authenticated, How can I set the token in a cookie so that user does not send username password in each request?
2) What is the ideal way of sending token to the client side?
apiRoutes.post('/authenticate', function (req, res) {
User.findOne({
email: req.body.email
}, function (err, user) {
if (err) throw err;
if (!user) {
res.send({ success: false, message: 'Authentication failed. User not found.' });
} else {
// Check if password matches
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var claims = {
sub: user._id,
email:user.email,
iss: 'https://NodeLogin.com',
permissions: user.role
};
var token = jwt.sign(claims, config.secret, {
expiresIn: 60 // in seconds
});
res.json({ success: true, token: 'JWT ' + token });
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
}
});
}
});
});
apiRoutes.get('/dashboard',
passport.authenticate('jwt', { session: false }), function (req, res) {
res.send('Worked' + req.user._id + '.');
});
you should follow code:
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var claims = {
sub: user._id,
email:user.email,
iss: 'https://NodeLogin.com',
permissions: user.role
};
var token = jwt.sign(claims, config.secret, {
expiresIn: 60 // in seconds
});
res.cookie('jwt',token); // add cookie here
res.json({ success: true, token: 'JWT ' + token });
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
}
});
and passport config:
var cookieExtractor = function(req) {
var token = null;
if (req && req.cookies) token = req.cookies['jwt'];
return token;
};
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = cookieExtractor; // check token in cookie
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
it's working for me :)
For httpOnly, signed, secure Cookies you might need to use signedCookies
const cookieExtractor = function (req) {
let token = null;
if (req && req.signedCookies && req.signedCookies.jwt) {
token = req.signedCookies['jwt']['token'];
}
return token;
};

Resources