cannot access protected route with passport-jwt - express

I'm not able to authenticate the secret resource, I'm calling with login token still I'm getting unauthorized in response every time.
passport.use(
new JwtStrategy(
{
jwtFromRequest: ExtractJwt.fromHeader("Authorization"),
secretOrKey: jwtSecret
},
async (payload, done) => {
try {
const user = await User.findById(payload.sub);
console.log(payload.sub);
if (!user) {
return done(null, false);
}
done(null, user);
} catch (error) {
done(error, false);
}
}
)
);
controllers/users.js :
const signToken = user => {
return jwt.sign(
{
iss: "nikname",
iat: new Date().getTime(),
sub: user._id,
exp: new Date().setTime(new Date().getDate() + 1)
},
jwtSecret
);
};
route :
router.route('/secret')
.get(passport.authenticate('jwt',{session: false}),usersController.secret);
I'm not able to figure out the problem since the error is unclear.
any help ?
Thanks a lot

after using the jwt debugger , it appeared that there was an issue with payload , although the debugger showed a verified token sign, but the 'exp' and 'iat' showed wrong dates, so I changed the signToken constant like this :
const signToken = user => {
return jwt.sign(
{
iss: "nikname",
sub: user.id,
},
jwtSecret,
{
expiresIn: '2d'
}
);
};
also after researching, it appeard that fromHeader (the extractor) is not well functionning in passport-jwt#4.0.0 so I used fromAuthHeaderWithScheme instead . Like this :
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'),
and now it is working just fine .

Related

Sequelize model property undefined Express.js controller after auth with passport-jwt

I am using passport-jwt to verify access to a given route in express.js, and then return a Sequelize model to the final controller. The code looks like:
The auth strategy:
const passportStrategy = passport => {
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.auth.ACCESS_TOKEN_SECRET
};
passport.use(
new Strategy(options, async (payload, done) => {
try {
const user = await User.findOne({ where: { email: payload.email }});
if (user) {
return done(null, {
user
});
}
return done(null, false);
}
catch (error) {
return done(error, false)
}
})
);
};
The route with the auth middleware
router.get('/:user_id/psy', passport.authenticate('jwt', { session: false }), patientsController.getPatientPsy);
The controller function
const getPatientPsy = async (req, res) => {
const authenticatedUser = req.user;
if (authenticatedUser.userType !== "patient") {
res.status(500).send("Big time error");
}
}
If I console.log(authenticatedUser) in the getPatientPsy() controller it successfully prints the Sequelize model with it's dataValues and so on, but when I try to access any property, be it userType or any other it consistently returns undefined.
In the passport-jwt authentication once a User has been found that matches the extracted JWT token, afaik it is returned synchronously and made it available in the req.user object, and I can print it with console.log, but why can't I access the model's properties?
I've tried to make the getPatientPsy() controller a sync function but it doesn't work either.
Thank you.
All right this is embarrassing, by default Passport.js returns the done(null, user) in the req.user property, and since I am returning { user }, I had to access through req.user.user.

Migrate ADAL.js to MSAL.js

I have a SPA which uses the solution provided here to authenticate with Azure AD and everything works as expected. Now I want to migrate this to use MSAL.js.
I use below for login:
import * as MSAL from 'msal'
...
const config = {
auth: {
tenantId: '<mytenant>.com',
clientId: '<myclientid>',
redirectUri: <redirecturi>,
},
cache: {
cacheLocation: 'localStorage',
}
};
const tokenRequest = {
scopes: ["User.Read"]
};
export default {
userAgentApplication: null,
/**
* #return {Promise}
*/
initialize() {
let redirectUri = config.auth.redirectUri;
// create UserAgentApplication instance
this.userAgentApplication = new MSAL.UserAgentApplication(
config.auth.clientId,
'',
() => {
// callback for login redirect
},
{
redirectUri
}
);
// return promise
return new Promise((resolve, reject) => {
if (this.userAgentApplication.isCallback(window.location.hash) || window.self !== window.top) {
// redirect to the location specified in the url params.
}
else {
// try pull the user out of local storage
let user = this.userAgentApplication.getUser();
if (user) {
resolve();
}
else {
// no user at all - go sign in.
this.signIn();
}
}
});
},
signIn() {
this.userAgentApplication.loginRedirect(tokenRequest.scopes);
},
And then I use below to get the token:
getCachedToken() {
var token = this.userAgentApplication.acquireTokenSilent(tokenRequest.scopes);
return token;
}
isAuthenticated() {
// getCachedToken will only return a valid, non-expired token.
var user = this.userAgentApplication.getUser();
if (user) {
// get token
this.getCachedToken()
.then(token => {
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
// get current user email
axios
.get('<azureapi-endpoint>' + '/GetCurrentUserEmail')
.then(response => { })
.catch(err => { })
.finally(() => {
});
})
.catch(err => { })
.finally(() => { });
return true;
}
else {
return false;
}
},
}
but after login I get below error:
Access to XMLHttpRequest at 'https://login.windows.net/common/oauth2/authorize?response_type=code+id_token&redirect_uri=<encoded-stuff>' (redirected from '<my-azure-api-endpoint>') from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Also the token that I get seems to be invalid as I get 401 errors trying to call api using the token. Upon checking the token against https://jwt.io/ I get an invalid signature.
I really appreciate anyone's input as I've already spent good few days and haven't got anywhere yet.
I'm not sure if this is your issue. however, for msal.js, in the config, there is no tenantId parameter, it's supposed to be authority. Here is a sample for graph api using msal.js
https://github.com/Azure-Samples/active-directory-javascript-graphapi-v2
specifically: the config is here: https://github.com/Azure-Samples/active-directory-javascript-graphapi-v2/blob/quickstart/JavaScriptSPA/authConfig.js
as per here, https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-js-initializing-client-applications it is supposed to be hitting login.microsoftonline.com not login.windows.net

Bcrypt + Sequelize password not saving as hash in DB

Sequelize + Bcrypt not storing passwords in DB as hash
As the title says, whenever I attempt to store a user into my SQLite DB the console outputs the password as a hash but when I look into the DB with DBbrowser I can see the plaintext password.
Model
// const Promise = require('bluebird')
const bcrypt = require('bcrypt')
async function hashPassword (user, options) {
if (!user.changed('password')) {
return 0
}
const SALT_FACTOR = 8
await bcrypt.hash(user.password, SALT_FACTOR, (err, hash) => {
if (err) {
console.log(err)
}
// user.setDataValue('password', hash)
user.password = hash
console.log(user)
})
}
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true
},
password: DataTypes.STRING
}, {
hooks: {
beforeSave: hashPassword,
beforeCreate: hashPassword
}
})
User.prototype.comparePassword = function (password) {
bcrypt.compare(password, this.password, function (res, err) {
if (res) {
console.log(res)
} else {
console.log(err)
}
})
return bcrypt.compare(password, this.password)
}
return User
}
Controllers
module.exports = {
async register (req, res) {
try {
const user = await User.create(req.body)
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (err) {
// e-mail already exists or such
res.status(400).send({
error: 'This email address is already in use'
})
}
},
async login (req, res) {
try {
// Grab user input
const { email, password } = req.body
const user = await User.findOne({
where: {
email: email
}
})
// Check to see if user is in db
if (!user) {
res.status(403).send({
error: 'the login information was incorrect / Not Found'
})
}
// Check to see if password is valid
const isPasswordValid = await user.comparePassword(password)
if (!isPasswordValid) {
return res.status(403).send({
error: 'The login information was incorrect'
})
}
// return user using toJSON()
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (e) {
res.status(500).send({ error: 'An error occured attempting to login' })
console.log(e)
}
}
}
To elaborate a little more, whenever I create a user, I receive the following:
{
"user": {
"id": 1,
"email": 'test#test.com",
"password": "$2b$08$SYYXU/GDSCFsp3MVeuqrduI0lOLHeeub7whXiaMMoVxO53YJry.1i",
"updatedAt": "2018-09-07T22:44:12.944Z",
"createdAt": "2018-09-07T22:44:12.944Z"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJTVVBCUkhVQGxvbC5jb20iLCJwYXNzd29yZCI6IiQyYiQwOCRTWVlYVS9HRFNDRnNwM01WZXVxcmR1STBsT0xIZWV1Yjd3aFhpYU1Nb1Z4TzUzWUpyeS4xaSIsInVwZGF0ZWRBdCI6IjIwMTgtMDktMDdUMjI6NDQ6MTIuOTQ0WiIsImNyZWF0ZWRBdCI6IjIwMTgtMDktMDdUMjI6NDQ6MTIuOTQ0WiIsImlhdCI6MTUzNjM2MDI1MywiZXhwIjoxNTM2OTY1MDUzfQ.mDaeIikzUcV_AGTuklnLucx9mVyeScGpMym1y0kJnsg"
}
Which to me says the DB successfully hashed my password, and stored it. The overhanging issue for me with this is the fact that I believe it's causing the bcrypt.compare function to spit out 'false'. As always, any insight or help would be greatly appreciated!
I'm pretty sure that this answer is too late for you, but might help others landing on this same question.
The main issue I can see is how you are using the async/await pattern. Changing this:
async function hashPassword (user, options) {
if (!user.changed('password')) {
return 0
}
const SALT_FACTOR = 8
await bcrypt.hash(user.password, SALT_FACTOR, (err, hash) => {
if (err) {
console.log(err)
}
// user.setDataValue('password', hash)
user.password = hash
console.log(user)
})
}
to this, worked for me:
async function hashPassword(user, options) {
if (!user.changed("password")) {
return 0;
}
user.password = await bcrypt.hash(user.password, SALT_FACTOR);
}
Can you please try to add only one hook
hooks: {
beforeSave: hashPassword,
}
Because I think your password is getting hashed two times. as beforeSave and beforeCreate both hooks get executed.
Hope it helps

socket.io callback is not recevied in vue.js method?

Using this vue.js method to login users:
loginUser: function () {
socket.emit('loginUser', {
email: this.email ,
password: this.password
}, function() {
console.log('rooms in callback are:', rooms);
});
}
On the server the loginUser event is handled by:
socket.on('loginUser', (newuser,callback)=> {
var body = _.pick(newuser, ['email', 'password']);
console.log('body is:', body);
User.findByCredentials(body.email, body.password).then((user) => {
return user.generateAuthToken().then((token) => {
if (token) {
console.log('token was found');
let rooms = ['Cats', 'Dogs', 'Birds'];
callback(rooms);
} else {
socket.emit('loginFailure', {'msg' : 'Login failure'});
}
}).catch((e) => {
throw e;
});
}).catch((e) => {
socket.emit('loginFailure', {'msg' : 'Login failure'});
throw e;
});
});
I can see 'token was found' printed out in the console but does not recieve the rooms being printed in the browser console. I receive no errors either.
I'm wondering whetehr it is due to how vue.js methods work? And if so, if there is a way around it?
You forgot to specify rooms as argument in the callback
loginUser: function () {
socket.emit('loginUser', {
email: this.email ,
password: this.password
}, function(rooms) { // need to have rooms argument
console.log('rooms in callback are:', rooms);
});
}

mocha : testing a signup process with email-verification framework

I try to test my signup router post function which use email-verification framework,but mocha shows me the error message below :
TypeError: Cannot read property 'findOne' of null
This my code :
function that invoke the error:
nev.resendVerificationEmail(email,(err,userExist) =>{
if(err){
return res.status(404).send('Error : resending verification email failed');}
if(userExist){
res.json({message : 'An email has been sent to you , again. Please check it to verify your account'});
}else{
res.json({message : 'Your verification code has expired . Please sign up again'});
and this is the implementation of resendVerificationEmail function
var resendVerificationEmail = function(email, callback) {
var query = {};
query[options.emailFieldName] = email;
options.tempUserModel.findOne(query, function(err, tempUser) { //this the error handler I guess
if (err) {
return callback(err, null);
}
// user found (i.e. user re-requested verification email before expiration)
if (tempUser) {
// generate new user token
tempUser[options.URLFieldName] = randtoken.generate(options.URLLength);
tempUser.save(function(err) {
if (err) {
return callback(err, null);
}
sendVerificationEmail(getNestedValue(tempUser, options.emailFieldName), tempUser[options.URLFieldName], function(err) {
if (err) {
return callback(err, null);
}
return callback(null, true);
});
});
} else {
return callback(null, false);
}
});
};
and this is my spec
describe(' SignUp : /POST Test with fake client request : ', () => {
let req, res, statusCode, sendData,user;
beforeEach((done) => {
SignupModal.remove({}, (err) => {
done();
});
user = {
firstName : 'ben',
secondName : 'wissem',
username : 'wiss',
email : 'xy#zt.sq',
password : 'wissem'
};
res = {
json: function (code, data) {
statusCode = code;
sendData = data;
}
};
});
it('should send 200 code', () => {
chai.request(server)
.post('/user/signup')
.send(user)
.end((err, res) => {
res.statusCode.should.equal(200);
});
});
Can any one help me please ?!
if you want to end-to-end test email verification try EmailE2E.com, it's a free API that let's you send and receive emails from randomly generated inboxes. It's perfect for Firebase, Amazon Cognito, or other OAuth providers that use email verification codes during sign up. It also has a Javascript client you can use with Mocha.