Reputation: 47
I am working on implementing custom error handling in a MongoDB MERN application. I’m also using Mongoose and passport-local-mongoose. When a user registers for a new account, they should get a specific error message in the chrome console saying, ‘username already exists’. I know it is a bad idea to show all the server error messages to the user, but I would like to show a select few.
Links that I have tried: https://github.com/saintedlama/passport-local-mongoose
Passport-local-mongoose : Authenticate user right after registration
https://www.geeksforgeeks.org/nodejs-authentication-using-passportjs-and-passport-local-mongoose/
Here is the code: server/controller/auth.js
const { User } = require('../models');
const register = async function (req, res) {
try {
const user = new User({ username: req.body.username });
await user.setPassword(req.body.password);
await user.save();
}
catch (err) {
console.log(`error inside save ${err}`);
res.status(500).send(err);
}
};
const login = async function (req, res) {
//code block under construction
console.log(`login!!!!!`);
};
const logout = function (req, res) {
req.session.destroy();
res.end();
};
exports.login = login;
exports.register = register;
exports.logout = logout;
server/models/User.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new Schema({
username: { type: String, unique: true },
password: { type: String}
});
userSchema.plugin(passportLocalMongoose);
const User = mongoose.model('User', userSchema);
module.exports = User;
server/routes/api/auth/index.js
const router = require('express').Router();
const passport = require('../../../config/passport');
const authController = require('../../../controllers/auth');
router.route('/logout').get(authController.logout);
router.route('/register').post(authController.register);
router.use(passport.authenticate('local', {
session: true
}));
// Matches with '/api/auth'
router.route('/login').post(authController.login);
module.exports = router;
server/server.js
const path = require('path');
const express = require('express');
const passport = require('./config/passport');
const mongoose = require('mongoose');
const cors = require('cors');
const session = require('express-session');
const helmet = require('helmet');
const morgan = require('morgan');
const corsOptions = require('./config/cors.js');
const routes = require('./routes');
const { v1: uuidv1 } = require('uuid');
// console.log(uuidv1());
const PORT = process.env.PORT || 3001;
const app = express();
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost/puzzlegallery', {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
});
mongoose.set("useCreateIndex", true);
// Define middleware here
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(helmet({ contentSecurityPolicy: false }));
app.use(session({ secret: 'sassy', resave: false, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors(corsOptions));
app.use(morgan('dev'));
app.use(routes);
// for Reactjs ##################
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
}
// #################################################
if (process.env.NODE_ENV === 'production') {
app.get('*', (_, res) => {
res.sendFile(path.join(__dirname, '../client/build/index.html'));
});
}
app.listen(PORT, (err) => {
if (err) throw err;
console.log(
`🌎 Server is Ready and Listening on http://localhost:${PORT}`
); // eslint-disable-line no-console
});
Here is a link to the repo also for more context: https://github.com/BenjDG/puzzle-gallery Thanks for any help you can offer!!!
Upvotes: 0
Views: 394
Reputation: 4786
If you want the caller to handle errors gracefully, you might consider returning a 200 response, like:
{
"success": true
}
or...
{
"success": false,
"errorCode": "UsernameAlreadyExists",
"message": "This username already exists."
}
It will be the responsibility of the caller to check the success
field to make sure the request succeeded. If you want, you can display the friendly message directly in your app, or you can use an "error code" to determine what to display. This is helpful for an API shared across multiple apps, and you want to display different messages, or if you support a multi-lingual UI, and want to translate the message.
Quick aside:
I know it is a bad idea to show all the server error messages to the user, but I would like to show a select few.
I'm sure you've seen articles that warn against this, so just a bit of clarification. You don't want to pass internal error messages and stack traces to your callers, as this exposes more information about your system than most clients should know. Attackers might use this information to learn more about your implementation, and use that to exploit your system.
In general, there is little harm in returning a friendly error message, or a sub-status code, esp for 4xx errors, to help the caller understand how they need to re-submit the request to get a successful response. The important thing is to abstract away all underlying implementation details, so don't just pass an error message directly from Mongoose to your caller, catch the exception, and send an appropriate response.
Because you're dealing with authentication - you also need to be careful about exposing too much to your caller. For example - exposing a "Check Username" endpoint will make it easy for someone to brute force your API to get a handful of valid users in your app.
Upvotes: 1
Reputation: 5411
catch
block is executed. In your code, it is.catch((err) => {
console.error(err);
});
So you have the error message in the console.
I think the error 500 is used in case you aren't aware of the error. In this case, we can handle the duplication error by checking before.
Upvotes: 1