I'm working on a Node.js app. Everytime I think I understand JavaScript, a curve ball is thrown at me that I don't understand. Currently, I'm trying to use passport.js to authenticate a user. I have the following code (which works):
passport.authenticate('google')(req, res, function() {
// TODO: Handle a user not authenticating
// This is the happy path. It will always execute. However, it should only execute if a user authenticated with google.
logger.info('user authenticated');
res.redirect(307, '/auth-confirm');
});
My challenge is, I need to detect when a user fails to authenticate with Google. If they succeed, I need to get the access token. However, I do not know how to do either of these because I do not understand the syntax of this call. Specifically, I don't understand what (req, res, function() {...}) is.
Is that line saying that passport.authenticate returns a function. So, req, res, and function() { ... } are passed as parameters to the function that passport.authenticate returns? If that's the case, I still don't know how to get the access token returned by google.
Related
So, if a new user tries to sign up with an user account that already exists, MongoDb responds with a 11000 error code
In Express, that can be handled like this:
async function signup(req, res, next){
try{
// some stuff
}catch(err){
if (err.code === 11000) {
err.message = 'Email is already taken.';
res.statusCode = 409;
}
return next(err);
}
}
For this example, I decided to respond with a 409 http status code.
However, I am not sure if it's a good approach to handle several different codes from MongoDB and assigning an http status for each of them.
What other solutions am I missing?
You can return specific responses to common errors that you might come across.
But it really comes down to how specific you want to be. You also need to consider is it really worth it to customize the response for each and every single error that can occur. Some of them might never happen depending on your configuration.
For example, ncompatibleShardingConfigVersion will never happen if you are not using a sharded cluster.
Furthermore, if the error message is supposed to be displayed at the frontend, the users don't really care about the what, why, and how of an error. What he/she knows is that it doesn't work and he/she is not happy.
You have several options:
Using conditionals like what you are doing now. Perhaps create a custom error constructor and put the conditionals in it to avoid having to repeat yourself in every single function call.
Send a generic error message to the frontend with status code 500. Log the whole error object at the backend so you can know what went wrong. You are the person who actually cares about the why, what, how, when of an error.
Personally, I will go with option 2.
I am using a res.redirect('page.ejs'); and on my browser I get the message:
Cannot GET /page.ejs
I have not declared this in my routes file in the style of :
app.get('/page', function(req, res) {
res.render('page.ejs');
});
Should this be included in order for the res.redirect() to work?
When I do not use res.redirect() but res.render(), even if I have not the app.get() code, it still works.
so to understand this, let's look at what each of these methods do.
res.redirect('page.ejs');
// or, more correctly, you're redirecting to an *endpoint*
// (not a page. the endpoint will render a *page*) so it should be:
res.redirect('/page');
this will tell express to redirect your request to the GET /page.ejs endpoint. An endpoint is the express method you described above:
app.get('/page', function(req, res) {
res.render('page.ejs');
});
since you don't have that endpoint defined it will not work. If you do have it defined, it will execute the function, and the res.render('page.ejs') line will run, which will return the page.ejs file. You could return whatever you want though, it can be someOtherPage.ejs or you can even return json res.json({ message: 'hi' });
res.render('page.ejs');
this will just respond to the client (the front-end / js / whatever you want to call it) with the page.ejs template, it doesn't need to know whether the other endpoint is present or not, it's returning the page.ejs template itself.
so then, it's really up to you what you want to use depending on the scenario. Sometimes, one endpoint can't handle the request so it defers the request to another endpoint, which theoretically, knows how to handle the request. In that case redirect is used.
hope that makes sense and clarifies your confusion
(I'm not an expert on the inner-workings of express, but this is a high-level idea of what it's doing)
You should do res.redirect('/page')
In the process of learning the MEAN stack I came across an issue.
Whenever I try to use passport authenticate method it never returns any response. I always get "localhost didn’t send any data. ERR_EMPTY_RESPONSE".
Here is the exact isolated code snippet that isn't working:
app.post("/login", passport.authenticate("local",
{successRedirect: "/campgrounds", failureRedirect: "/login"}),
function(req, res){
});
If you want to see the whole code, you can find it: HERE
Does anyone have any solutions?
this line needs parens after authenticate
according to the docs here
I'm new to JS and new to node. This seems a stupid question since I have not found others have the same question as I do. Anyway, my goal is to understand it and use the tool.
I'm trying to use PassportJS. However, when I look at the guide and source code. I'm not able to find how the Verify Callback: done is defined. There is an intro for Verify Callback and some comments in the source code like this:
Callbacks:
This middleware requires an issue callback, for which the function
signature is as follows:
function(client, username, password, scope, done) { ... }
client is the authenticated client instance attempting to obtain an
access token. username and password and the resource owner's
credentials. scope is the scope of access requested by the client.
done is called to issue an access token:
done(err, accessToken, refreshToken, params)
accessToken is the access token that will be sent to the client.
An optional refreshToken will be sent to the client, if the server
chooses to implement support for this functionality. Any additional
params will be included in the response. If an error occurs,
done should be invoked with err set in idomatic Node.js fashion.
Do I need to declare done myself? Like function(client, username, password, scope, function done(xx,xx...) {...}) { ... }
Or it is already declared? In this case, what does done return? Where can I find its declaration?
I guess I have not fully caught some ideas in async JS which leads to my current understanding challenge.
Thanks in advance,
Liwei
EDIT 1:
done(xx, xx...) { This is the part I'm looking for, my current guess is I need to write this myself, since I couldn't find this part in source code }
I understand you're trying to implement whats called local strategy (vs social one), see an example of usage at: http://passportjs.org/guide/username-password/
I am using passport-local-mongoose and trying to setup a simple working example using the login example in the repository. But I'm running into problems and I get an error message saying "Error: failed to serialize user into session".
I have reproduced the problem in a gist. (To run the gist, you will need to replace the mongodb server IP and database name in the user.js file and do a POST to the /register endpoint with username and password values.)
In it you will see the endpoints '/setval' and '/getval' which set values in the session and retrieves it, showing that session support is working. The endpoint '/authtest' gives an Unauthorized response, even after doing a POST to '/login'. The endpoint '/authdebug' gives more information - the error mentioned above.
Anyone have any ideas on what is going on? I'm running out of things to try.
I think that passport.authenticate should only be used on routes which are actually used for authenticating the user; in your case, that would be the /login route.
For routes for which you want to make sure a user has previously authenticated, like /authtest, you need a middleware which checks if a user is authenticated:
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login'); // or your login page
}
// and
app.get('/authtest', ensureAuthenticated, function(req, res) {
...
});
An alternative for that middleware would be connect-ensure-login.