My webpage requires that people log in to access the website. Some pages can only be viewed when a user logs in. For example, here is a route to a profile page that can only be viewed once logged in (express):
router.get('/profile', function (req, res) {
User.findOne({_id: req.user._id}, function (err, user) {
if (err) {
return next(err)
} else {
res.render('main/profile', {user: user});
}
});
});
If someone tries to access the /profile page without logging in, they will get a page with a bunch of errors on it.
What I want to be able to do is, instead of showing a page of random errors, direct these types of users to a proper error page. I've tried replacing return next(err) with something like res.json(You cannot access this page'), but that doesn't seem to be working.
This is the answer to my question, using the link in the comment section above. Basically, when someone tries to access parts of my website without logging in, they will be re-directed to an error page located in the folder errors and the filename 500. And because this specific type of error is a 500 code error, we used res.status(500). here is the complete code (which goes in the main server file):
app.use(function(err, req, res, next) {
res.status(500);
res.render('errors/500');
});
Related
I am getting data from an API and am displaying it on my local server.
Below is my code to get data which matches the ID from the API data:
router.get('/:id', async (req, res) => {
checkString(req.params.id)
try {
const person = await peopleData.getPersonById(req.params.id);
res.json(person);
} catch (e) {
res.status(404).json({ message: 'There is no person with that ID' });
}
If there is no match I want to display the message like in the catch block, but the code does not go there as not getting a match is not an error technically.
So I tried the below code to get this message:
router.get('/:id', async (req, res) => {
checkString(req.params.id)
try {
const person = await peopleData.getPersonById(req.params.id);
if(!person) res.json('There is no person with that ID'); // Added new line here
res.json(person);
} catch (e) {
res.status(404).json({ message: 'There is no person with that ID' });
}
This does the work but it prints the message with quotes around as a string, is there a way I can display the message in the catch block if no match is found?
You can throw an error and the catch will display it.
if(!person) throw new Error("There is no person with that ID");
....
then in the catch...
catch(e){
res.status(404).json({ message: e.message })
}
If you're sending people to a fullscreen "error stack" page, then you may not need to use res.json()! You can also use res.send()
if(!person){ res.send('<p>There is no person with that ID</p>'; return; }
// Or
if(!person){ res.send('There is no person with that ID'; return; }
You are returning Json responses, so it looks like your consumer is not a web page but another app. If so, you should return undefined or null if there is no person found, and let the web page or consumer decide what message to show. Reasons are:
It should be easier to modify web pages than code, and typically the UI or marketing people will always want to fine tune (usually many times) every message on a web page.
Your app is an API app. The place where the user not found message is to be shown can be many steps away. Or it may be inappropriate to show the message at all, for example the consuming app might want to redirect to/show a registration page instead if user is not found.
Your web site may be multi-lingual, and you don't want the back-end to be involved in this.
"User not found" in many situations is not really an error, but it all depends on your application.
The catch block in your case should be used to handle other errors, for example, your database server might be down, or the database request might have timed out, etc etc. Your current code will misleadingly show "user not found" if there is a database error!
I would also let the Express error handler take care of such real errors, instead of coding error handling for every API function you have:
router.get('/:id', async (req, res, next) => {
checkString(req.params.id);
try {
const person = await peopleData.getPersonById(req.params.id);
res.json(person); // assuming getPersonById returns null if user not found
} catch (e) {
next(e);
});
Your Express error handler, where the invocation of the above next function lands, should be something like this (asssuming router is your Express app):
router.use((err, req, res, next) => {
let statusCode = err.status || 500;
// Assuming your app need to return only json responses
res.json(err);
});
I have some middleware which uses passport.js, which aims to authenticate a user, then move onto the next piece of middleware:
exports.authenticate = (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
console.log('You are authenticated!!')
next()
})(req, res, next)
}
When the user registers, I see You are authenticated!! in my console. So by this logic, the user should be attached to the req. So I call next and it moves onto this piece of middleware (I want to do something else before the user is redirected):
exports.createMatch = async (req, res) => {
console.log(req.user._id)
}
However, an error on my console and webpage shows TypeError: Cannot read property '_id' of undefined. Why is this and how do I rectify it?
routes.js:
router.post(
'/register',
userController.validateRegistration, // validate them
userController.register, // register them to the db
authController.authenticate, // authenticate them
catchErrors(dataController.createMatch) // do some other bits then redirect
)
Fairly new to Express. If more code is needed let me know. Apologies if something similar was answered elsewhere.
Regards,
James.
This is the line in the source where req.user gets set:
https://github.com/jaredhanson/passport/blob/821a474342b1ae900849911b5c3d3ccc4ef5ab86/lib/http/request.js#L44
It's in the method req.login. The documentation is here:
http://www.passportjs.org/docs/login
It states:
When the login operation completes, user will be assigned to req.user.
Further it says:
passport.authenticate() middleware invokes req.login() automatically.
So far everything sounds like it should work...
However, if you read the section about providing a Custom Callback, which is what you're doing, it states:
Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response.
There are several ways to fix it. You could get rid of the custom callback, you could call login inside the callback, or you could just set req.user = user yourself.
(When I run same code on ubuntu with node 4.2.6. I don't see this problem. )
I found some has similar problem: Express.js close response
==============problem description=========================
I encountered a very strange problem.
In my Express project, I am using passport package.
As long as I signed in myself, then every page will keep loading for 2 minutes.
If I am not logged in, I found pages behave normally.
/* GET home page. */
router.get('/', function (req, res, next) {
res.render('index', {
title: 'FASIDS',
activePage:'Home',
isAuthenticated: req.isAuthenticated(),
user: processReqUser(req.user)
}, function (err, html) {
if (err) {
next(err);
return;
}
res.send(html);
});
});
I have to substitute res.send(html) with res.end(html) to make my code behaves normally in both situations ("signed in" or "not signed in").
Really have no idea how to deal with it. Please help.
Additionally, please refer my processedPassport.js to see how I am handling authentication.
The passport is used in app.js as following:
...irrelevant code..
var passport = require('./components/processedPassport.js').addPassport(app, db_models);
...irrelevant code....
For reference, I included screenshots
Following pic shows a loading time of 2 mins
Following pic shows request and response headers
I am using passport.js for authentication. My requirement is that, anyone should not be able to access a particular page (say, '/restricted'), if one is not logged in.
Right now, in my application, anyone can access "localhost:3000/#/restricted" url directly.
I am able to stop this and allow only logged in users to access the page by using Rorschach120's solution in
Redirect on all routes to login if not authenticated.
But this is done client side and is not that secure, because anyone can access this code from browser.
So I need that the request for my page goes to server, I tried moka's solution in How to know if user is logged in with passport.js?:
In app.js:
app.get('/restricted', loggedIn, function(req, res, next) {
// req.user - will exist
// load user orders and render them
});
where the loggedIn() function checks if user is logged in or not.
But this middleware is NEVER called and anyone can still access the "restricted" page.
What can I do, so that this gets called?
I am new to AngularJS and NodeJS. Am I doing something wrong here?
Any help will be appreciated.
You can use middleware for that purpose.
app.get('/secure-route', secureMiddleware, myMethod)
let secureMiddleware = function(req, res, next) {
authCheck(...)
.then(function(result) {
// pass
next()
})
.catch(function(err) {
res.status(401).json({
code: 401,
message: 'restricted route'
})
})
}
I'm using Sails.js, and I've installed the passport and passport-local modules, for local authentication.
When I installed the passport module, some files were created automatically, under policies, services, models and controllers. I've modified some of these files to fit my needs, however, I can't find a way to redirect an user to certain location specifically after he registers.
Right now, the default behaviour is that the user registers and is automatically authenticated and then redirected to the same url that an user gets redirected to when he/she logs in.
I've tried to look into the different passport generated files, but I can't find a "registration callback" or something similar. In the AuthController, there's a "callback" method that looks like this:
callback: function (req, res) {
function tryAgain (err) {
var flashError = req.flash('error')[0];
if (err && !flashError ) {
req.flash('error', 'Error.Passport.Generic');
} else if (flashError) {
req.flash('error', flashError);
}
req.flash('form', req.body);
var action = req.param('action');
switch (action) {
case 'register':
res.redirect('/signup');
break;
case 'disconnect':
res.redirect('back');
break;
default:
res.redirect('/login');
}
}
passport.callback(req, res, function (err, user) {
if (err) {
return tryAgain();
}
req.login(user, function (err) {
if (err) {
return tryAgain();
}
res.redirect('/');
});
});
},
However, this only applies to when the user is authenticated (login), I think.
I think the answer may lie under the passport file over at the services folder, but I'm not sure. I don't really fully understand the code yet.
I'm just looking for the right place to even place this code, since I have no clue. So, how can I redirect an user to a specific location after he has registered? (But ONLY after that)
Thanks.
Well, I don't know if this is the best way do to it, but I found it to be quite simple; this is what I did, in case anyone finds themselves under a similar situation with a similar setup.
Under api/services/protocols/local.js, there is a method named exports.register; within that method, and just before next() is called, declare a property isNew to user, like this:
user.isNew = true;
Then, in the callback method of the AuthController, under the controllers folder, isNew will be available (and set to true) as req.user.isNew, if the user just registered, and that way, you can do your logic with that, before res.redirect is called. So, something like this (within that method):
if(req.user.isNew) {
return res.redirect('/welcomePage');
}
return res.redirect('/dashboard');
And that's it.