Using Promises / Node - javascript

I'm trying to find a way/best way to use promises in my Node application.
I'll be honest, I don't 100% understand promises, but I'm getting better with more practice.
What I'm currently looking to do is after someone logs in:
Get a facebook access token
Access the users profile, update the user document in Mongodb or create a new user document if they don't exist
Access facebook friends list and cross reference a "friends" document in Mongodb.
Problem I have is to do point 3, I require the access token from point 1 AND the user document either existing or newly created in point 2 to pass into the third function.
My chain of functions looks like:
getAccessToken(req)
.then(function(accessToken){
getUserProfile(req, accessToken)
.then(findUser.bind(null, req, res))
.then(...)
});
getAccessToken returns "accessToken" from Facebook.
getUserProfile returns the user from Facebook.
findUser finds existing or creates new user Document in Mongodb.
How do I return the existing or newly created user object and pass it through coupled with the accessToken in the form of a promise?

You can either nest promises:
getAccessToken(req)
.then(function(accessToken) {
return getUserProfile(req, accessToken)
.then(findUser.bind(null, req, res))
.then(function(user) {
// use accessToken & user
})
});
Or store resolved values in the outer scope:
var _accessToken;
getAccessToken(req)
.then(function(accessToken) {
_accessToken = accessToken;
return getUserProfile(req, accessToken);
})
.then(findUser.bind(null, req, res))
.then(function(user) {
// use _accessToken & user
});
Remember to always return the nested promise.

Related

How can I access express request object in Mongoose virtual get func

I tried to find this, but I could not find a solution which will answer my doubt.
I have a virtual mongoose property defined as :
postSchema.virtual('permissions').get() => {
});
What I am trying to achieve is fill this permissions property of string array type, with a list of permissions that user has on each post. This logic is derived through the owner user id, which is stored in the post mongo database and the user id present in the request object of express, which is coming from the requestor.
However, I realised that the request object is not available to virtual method:
postSchema.virtual('permissions').get((req) => {
// req is null.
});
Now, I do have a solution to fix this issue by making a find result to lean at service level.
app.get('/api/posts', (req, res) => {
PostModel.find({}, '-fbEmailAddress', { sort: { created_at: -1 } }).lean().exec(function (err, posts) {
posts.array.forEach(element => {
element.permissions = // write your permission logic here
});
res.send(posts);
});
});
However, if I do so, it will not stop calling . any virtual properties that I have also defined. I loose the opportunity to manipulate firstname, lastname to fullname et. al.
Do you guys have any recommendations to resolve this in a nicest possible way, so that I have an access to the current user id coming in the request object?
Please note that it is an API, so I don't want to introduce any kind of session object.
Cheers
I think you should look at this module https://github.com/othiym23/node-continuation-local-storage
It allows to set context variables in your request middleware and then use them in all functions called from it, e.g. mongoose model handlers.

Passport-Google-OAuth Callback Not working when used in Web Service

I Have used Passport-Google-OAuth in Node.js web service project. I am using OAuth2Strategy.
The process i have used is i call the web service method to authenticate user from his Gmail account. Initially i serve the Raw HTMl which i receive from calling the Passport-google-OAuth. Which works fine.
Then i login with valid Gmail accounts. Once the Callback Url is called by google the server goes into infinite loop and calls the callback url again and again after fixed interval of time.
My Passport strategy configuration for Google is like this:
// Use the GoogleStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Google
// profile), and invoke a callback with a user object.
passport.use(new GoogleStrategy({
clientID : "948630708036-2t6mestiv81gtv0s9n6iptoava4o1cpa.apps.googleusercontent.com",
clientSecret : "omugRnr7nad2yMmefiZdBaLL",
callbackURL : "http://localhost:4000/api/auth/google/callback"
},
function(token, refreshToken, profile, done) {
console.log('Inside global callback.');
// make the code asynchronous
// User.findOne won't fire until we have all our data back from Google
process.nextTick(function() {
// try to find the user based on their google id
User.findOne({ 'google.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
// if a user is found, log them in
return done(null, user);
} else {
// if the user isnt in our database, create a new user
var newUser = new User();
// set all of the relevant information
newUser.google.id = profile.id;
newUser.google.token = token;
newUser.google.name = profile.displayName;
newUser.google.email = profile.emails[0].value; // pull the first email
return done(null, newUser);
}
});
});
}));
Then i am calling the Passport from the endpoint in the service project:
passport.authenticate('google', { session:false,scope : ['profile', 'email'] });
And the Callback URL contains the following code where i am sending the returned Google account details of the user in JSON format to the client which accessed the web service intially.
function(req, res) {
console.log('Callback by Google:'+res.body+' || '+ res.headers);
console.log('Response Object:'+util.inspect(res));
passport.authenticate('google', { session : false }),function(req,res){
console.log('Callback authenticated.User: +req.user);
res.json(req.user);
}
In the Log i am getting "Callback by Google: undefined || undefined".
I am disabling sessions since this will be the API Server feeding data to various clients.
I dont know what mistake i am doing. Kindly point out any resource or example where the Passport-Google-OAuth(OAuth2Strategy) is used in a API(Web Service) server. Do i need to follow some other way. Thanks for ur help in advance.
There may be a problem in your routes. Look at the tutorial here
https://scotch.io/tutorials/easy-node-authentication-google
It's the best I have seen. And I have implemented something similar.

Mongoose pass req object to middleware

I am writing a middleware for mongoose that gets executed for every find object using pre query hook.
postSchema.pre('query', function(query, next) {
// I want to access the req.user object here
query.populate('Category');
next();
});
I want to access req.user object inside the pre for every request made to the api server. How can i pass the object to the middleware?
Is it even possible?
https://github.com/Automattic/mongoose/issues/1931
I found the above but it doesnt talk about passing req object.
====================
Edit after some confusion about the question.
What i am trying to accomplish is to get the req.user role and the model name pass it to another function to get the query condition for find. So depending on the user role and the type of model accessed the query condition will change.
Wrap the middleware in another middleware that has access to req.
Something like, assuming express
router.verb('/some-route', function (req, res, next) {
postSchema.pre('query', function(query, next) {
console.log(req);
query.populate('Category');
next();
});
});
Edit
- Attach this only to the route that you want the prehook for.
Disclaimer - Not tested.
I know i'm joining this party late but you can use a service to pass the data you want between the request object and the mongoose prehook method. In your service create private variables that hold the data that you want to pass. Set those variables in your custom middleware and call the service get method to get the values in the mongoose prehook method.
Use
query.op to get the type of query
query.options to get other options like {runValidators: true}
query._condition to get the conditions of the query
query._update to get the incoming body
query._fields to get the selected fields,
You can also log the query to the terminal to see various options
Yes, it is possible.
restify.serve(router, model, {
preCreate: function (req, res, next) {
req.body.createdBy = req.user._id
next()
}
})
Follow this doc

Parse - why does user.getSessionToken() return undefined

I'm using cloud function to fetch user's sessionToken. The documentation says, this method only returns correct value when a user object is fetched with the master key. However, even if I use the master key, I still get the undefined result. What's wrong with my code?
Parse.Cloud.define("hello", function(request, response) {
Parse.Cloud.useMasterKey();
Parse.Promise.as().then(function(){
var query = new Parse.Query(Parse.User);
return query.first({useMasterKey:true});
}).then(function(user){
return user.fetch({useMasterKey:true});
}).then(function(user){
response.success(user.getSessionToken());
});
});
That won't work because the user is not logged in when you fetch him via cloud code. For getSessionToken to work you need to already have a logged in user. Because, otherwise how would CloudCode know which session of your user you want to get? (if he is logged in multiple devices) Also your code is returning the first user in the database, is that realy what you want?
Either way, you need to log the user in via cloud code and then return the session token. However I had some trouble doing that without the password (just by calling user.logIn(), it should work so give it a try) so the only solution I found was to change the password of the user and then log him in. Why do you need to fetch the user's session token? If you don't mind changing the user's password you can do something like:
var password; //create new random passowrd
query.first({useMasterKey: true}).then(function(user){
user.set("password", password);
return user.save();
}).then(function(user){
return Parse.User.logIn(user.get("username"), password);
}).then(function(user){
return user.getSessionToken();
});

Passport js authenticate by url

I'm using Express JS and Passport JS for my app.
I want to give a new user the opportunity to automatically login, once, by a specific URL. I can get the user from the database with the information from the URL, so I have an User object (with id, email, hashed password etc.) but I don't know how I can use passport to authenticate the user and login.
I tried executing below function with the user object I got from the database:
req.login(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + req.user.username);
});
source: http://passportjs.org/guide/login/
But that didn't work. Guess it's just because the user object contains the hashed password...
Anyone who ever tried this before and can tell me how it works?
Maybe https://github.com/yarax/passport-url strategy will be useful for you
Base logic is getting argument from url
UrlStrategy.prototype.authenticate = function(req, options) {
var self = this;
function verified(err, user, info) {
if (err) { return self.redirect(self.failRedirect); } // redirect in fail
self.success(user, info); // done callback
}
this._verify(req.query[this.varName], verified);
};
Full example here https://github.com/yarax/passport-url/blob/master/index.js
Heyo, so while #Rax Wunter is totally right, I just saw this question and wanted to say it is NOT A GOOD IDEA to do what you're doing here. You should never be passing a hashed password in a URL string ever. This is a really bad security concern.
What you should do instead is use something like a JSON Web Token (JWT). There are lots of libraries to help with this, but the basic flow goes something like this:
Wherever you are generating your URL, you'll instead generate a JWT that contains the user ID in it.
You'll then build a URL that looks like: https://somesite.com/?token=
On your https://somesite.com endpoint, you'll read in the token, validate it using the JWT library (and a shared secret variable), and this will confirm this token was unmodified (eg: you KNOW this user is who they claim to be).
This strategy above is really great because it means you can safely log someone in, in a trusted way, without compromising security or leaking a password hash at all.
There is not need of any additional module or passport-strategy for this. Adjust below code according to your use case;
router.get('/url/:token', (req, res) => {
User.findOne({token: req.params.token}, (err, user) => {
req.login(user, {}, function(err) {
if (err) { console.error(err); }
else return res.redirect("/home.html");
});
});
});

Categories

Resources