I am using passport to handle user authentication. During initialization, I have to do the following for managing the session cookies:
passport.serializeUser((user, done) => done(null, user.ID));
passport.deserializeUser((id, done) => done(null, getUserById(id)));
Here, I have stored the users in my database, so getUserById is an asynchronous function, defined as follows:
const getUserById = async (id) => {
result = await executeQuery(`SELECT ID, USERNAME, PASSWORD FROM STUDENTS WHERE ID=:id`, [id]);
if (result.rows.length != 0) return result.rows[0];
else return null;
};
Here, getUserById(id) function does not immediately return the result from query execution, it returns a promise. What is the proper way to pass this getUserById() function to done()?
Should I wrap getUserById(id) in another IIFE async function?
Or should I do the following?
passport.deserializeUser(async (id, done) => {
const user = await getUserById(id);
done(null, user);
});
The first solution that comes to mind:
passport.deserializeUser((id, done) => {
getUserById(id).then(user => done(null, user))
};
I.e., handle the promise using then(). I don't know if deserializeUser will take an asynchronous call-back function. I do know some functions like useEffect in React don't.
Related
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.
I am wondering how express.js works when you specify multiple argument in the app.get, for example:
app.get('/', requireAuth, (req, res) => { })
Here, I believe you have the slash "/" as the route, and (req, res) as arguments for the callback function. But there is also the 'requireAuth' part which was used for authorization. But in the documentation I don't find anything about multiple arguments in the way we use 'requireAuth' now. It does mention that you can use an array of functions but that is not used here (https://expressjs.com/en/guide/routing.html). How does it work?
P.S. The 'requireAuth' code for reference:
module.exports = (req, res, next) => {
const { authorization } = req.headers;
// authorization === 'Bearer laksjdflaksdjasdfklj'
if (!authorization) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const token = authorization.replace('Bearer ', '');
jwt.verify(token, 'MY_SECRET_KEY', async (err, payload) => {
if (err) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const { userId } = payload;
const user = await User.findById(userId);
req.user = user;
next();
});
};
All the arguments are simply middle-ware functions that you call before the actual route logic is run(for individual routes). As long as there is next handling in each of the middle-ware functions, the chain will run till your route logic, and then exit where next isn't handled.
This is the magic of express. You can do many things with it, like error handling, handling different content types etc.
I can't get my jest test to pass even though the code works:
describe("Testing the API call", () => {
test("Testing the API call", () => {
sendToServer("Hey there!")
})
})
And jest throws me this:
ReferenceError: Request is not defined (it can't find Request constructor)
I'm pretty new to jest so I have only tried what I could find on stack overflow, but there are no solutions to this one. I tried to import Request from html and it didn't work.
It would be easier to help you if you would share your getData function, but let me assume that you are doing something like, for fetching your data:
async function getUsers() {
const URL = `https://randomuser.me/api/?results=10`;
try {
const response = await fetch(URL);
return response.json();
}
catch (e) {
console.log(e);
return {}
}
}
The above function will make a call to the Random User API and return an Object with an array of results, in our case 10 random users.
In order to test this function, you can write tests, such as:
describe('getUsers function', () => {
test('the data returned is an object', async () => {
const data = await index.getUsers();
expect(data).toBeInstanceOf(Object);
});
test('the Results array has 10 entries', async () => {
const data = await index.getUsers();
expect(data.results.length).toBe(10)
});
});
Here you will assert, that you do return Object from the call and that you do return correct number of users on call.
From previous answers this probably has something to do with my connection but I can't seem to place how to find the issue. I have a few segment of codes that looks like this and the result is a stall after any model function is called
connection - prints success when the server starts
mongoose.Promise = require('bluebird');
mongoose.connect('mongodb://localhost/test', { useMongoClient: true, promiseLibrary: require('bluebird') })
.then(() => console.log('connection succesful'))
.catch((err) => console.error(err));
model.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var holdingsSchema = new Schema({
pair: String,
amount: Number
});
var holdingsModel = mongoose.model('holdingsModel', holdingsSchema);
module.exports = holdingsModel
and api endpoint
holdingsModel.find(function(err, results) {
if (err) {
console.log('there is an error')
}
return res.json(results);
}).then(results => console.log('the results: ' + results));
});
At the end of everything I receive an err: empty response.
find() needs a criteria object. The second argument is the callback. When you are using promises you don't need the callback. Finally, you have to handle the errors with catch().
holdingsModel.find({ pair: 'test' }).exec()
.then(results => res.status(200).json(results))
.catch(err => res.status(400).json(err));
The exec() is used to return a proper promise, as mentioned here.
fetchFriends: {
type: new GraphQLList(UserType),
args: {
currentId: { type: new GraphQLNonNull(GraphQLID) }
},
resolve: (_, {currentId}) => {
return Promise.resolve()
.then(() => {
User.findById(currentId, (err, users) => {
users.getFriends((err, user) => {
console.log(user);
return user;
});
});
})
}
/* another version what i tried that returns only the initial findById user
resolve: (_, {currentId}) => {
var value = User.findById(currentId, (err, user) => {
new Promise((resolve, reject) => {
user.getFriends((err, user) => {
console.log('fetch: ', user);
err ? reject(err) : resolve(user)
});
})
})
return value;
}*/
},
I have a graphql resolve that i am getting the User object within the findById callback. that specific object calls getFriends which is a part of a mongoose plugin (friends-of-friends) and the console.log within getFriends callback contains the list in the terminal so i know getFriends
is returning the proper data but i cannot figure out how to return the value into my React-Native Component. i have tried for the past 8 hours everything i can think of and cannot get the value returned out of this function.
You're close, but there's a couple of things to keep in mind when working with resolvers:
Your resolver has to return either a value that matches the type/scalar specified in your schema or a Promise that will resolve to that value.
Mongoose operations can return a promises, and you should utilize this feature of them rather than trying to wrap callbacks inside Promises as this can easily get messy
Return statements inside callbacks at least in this context) don't actually do anything. Return statements inside a then, on the other hand, determine what the promise will resolve to (or what promise to invoke next in the chain).
I would imagine your resolver needs to look something like this:
resolve (_, {currentId}) => {
// calling exec() on the query turns it into a promise
return User.findById(currentId).exec()
// the value the promise resolves to is accessible in the "then" method
.then(user => {
// should make sure user is not null here, something like:
if (!user) return Promise.reject(new Error('no user found with that id'))
// we want the value returned by another async method, getFriends, so
// wrap that call in a promise, and return the promise
return new Promise((resolve, reject) => {
user.getFriends((error, friends) => {
if (error) reject(error)
resolve(friends)
})
})
})
}