Hi so it's kind of complicated for me, hope anyone can help.
Here's the situation : i have an app divided server side with node/express and front side with Vuejs,
what I'm doing in the back is creating a user here's the code :
const createUser=(req, res, next) => {
console.log("register");
let con=req.con
let { email,password } = req.body;
console.log(req.body)
con.query(
`SELECT * FROM users
WHERE email = $1`,
[email],
(err, results) => {
if (err) {
console.log(err);
res.status(404).json({error: err});
}
console.log(results);
if (results.rows.length > 0) {
//throw new error_types.InfoError("user already exists");
res.status(200).json({error: "user already exists"});
} else {
const hashedPassword = bcrypt.hashSync(password, parseInt(process.env.BCRYPT_ROUNDS));
con.query('INSERT INTO users (email,password) VALUES ($1, $2)',
[email,password],
(err, results) => {
if (err) {
next(err);
}
res.json({info: "User inseted" });
}
);
}
}
);
}
so im checking if it already exists else register it in DB,all good here.
Now in my Vuejs part i have this :
REGISTER({ commit, dispatch, rootState }, { payload }) {
const {email,password} = payload
console.log(payload)
commit('SET_STATE', {
loading: true,
})
const register = mapAuthProviders[rootState.settings.authProvider].register
register(email,password)
.then(success => {
if (success) {
notification.success({
message: "Succesful Registered",
description: "You have successfully registered!",
})
router.push('/auth/login')
commit('SET_STATE', {
loading: false,
})
}
if (!success) {
commit('SET_STATE', {
loading: false,
})
}
})
},
Now the problem happens here as the registration is done all okay but when i use the same email again for another registration it said the same message successfully registred but do not get saved to DB now what i want is the message user aleady exists that appears.
Anyone can help me please?
Edited :added axios part
export async function register(email,password) {
return axios
.post('/register', {emailpassword,})
.then(response => {
if (response) {
const { token } = response.data
if (token) {
store.set('accessToken', token)
}
return response.data
}
return false
})
.catch(err => console.log(err))
}
Without seeing the actual source code that does the HTTP request from the client, it's hard to say exactly what the error handling looks like. The most obvious culprit is this:
res.status(200).json({error: "user already exists"});
You're responding with HTTP 200 OK when an error occurs. Typically, a client implementation will treat this as success. You should signal to clients that an error has occurred - for example, respond with a "409 Conflict". Also make sure the client's fetch() call (or whatever the client uses for talking to the server) does proper error handling (checks statusCode).
The code has another issue, however - a race condition. This is a case of a TOCTTOU (Time-of-Check to Time-of-Use), where a non-zero amount of time passes between the existence check (SELECT) and the INSERT. If two users are registering for the same e-mail at the same time, they could both get a success.
You should remove the check altogether and use uniqueness constraints offered by the database instead (UNIQUE INDEX). This way, the DB guarantees there can be no duplicates and you don't have to worry about race conditions.
Related
Note: What you see below is the updated description of my problem, because I have been going down a rabbit hole and finding the root cause of a problem.
So, I found what's causing it (read 'OLD DESCRIPTION' below to know the context), but I have zero idea why is it being caused. So, the thing is, apparently Node cannot find the utils.getHash function (I have a separate file called utils.js which exports the getHash function), so it is never called, and execution never moves forward.
utils.js
...
const getHash = (password) => {
return crypto.createHash('sha3-512').update(password).digest('hex')
}
...
module.exports = {
getHash: getHash
}
Someone help please :(
OLD DESCRIPTION
There's a weird problem I am facing. I wrote a backend API server in ExpressJS, and one of the task it performs is user authentication. I am using MongoDB as the database, and Mongoose to connect and perform operations on it.
The problem I am facing is that the checkUserCreds function does not proceed after a point (commented in code), and Express just returns a blank JSON response.
And I say it it's weird, because I tested with the SAME code just 2 days back, it worked correctly like it should.
user.js
userSchema.statics.checkUserCreds = function (email, password) {
return new Promise((resolve, reject) => {
// Execution goes upto '$and' line, then it goes nowhere; no exceptions are raised
User.findOne({
$and: [{ email: email }, { password: utils.getHash(password) }]
}, (err, userDoc) => {
if (err) {
reject({ status: "ERROR", message: err })
} else if (userDoc) { // If valid credential
console.log(`User with email '${email}' logged in`)
resolve({ status: "OK", message: "Login successful!" })
} else { // If invalid credential
reject({ status: "ERROR", message: "Invalid credential!" })
}
})
})
}
api.js
// Route - Login (POST: email, password)
router.post("/login", (req, res) => {
// If user is already logged in, reject further login
if (req.session.email) {
res.json({ status: "ERROR", message: "Already logged in!" }).status(403).end()
} else {
// Get data from body
var form = formidable()
form.parse(req, (err, fields, files) => {
if (err) {
res.json({ status: "ERROR", message: err }).status(500).end()
} else {
// Check if credentials are valid
User.checkUserCreds(fields.email, fields.password).then((result) => {
// This portion of code isn't reached either
req.session.email = fields.email
res.json(result).status(200).end()
}).catch((err) => {
res.json(err).status(401).end()
})
}
})
}
})
Can anyone tell me why this is happening?
I'm using node and express. What I want to do is to do is make a mix of res.render and res.redirect.
Thing is, res.render can only receive a .ejs file and data, and redirect will go to a specific URL. What I need to do is go to a specific URL (e.g. /reviews/new/:id), render a .ejs file and give some data to it.
This is my code. I can't use session or cookies for this project.
This are the routes, user enters to edit a review of some show. If it is a new review, the id of the show is in the URL, if the user is editing one, the ID of the review is on the URL. Either way, if something fails, I have to append something to this URL and send data.
router.get('/new/:id', controller.newReview);
router.post('/store', controller.checkBeforeStoringReview);
router.get('/edit/:id', controller.editReview);
router.post('/update', controller.checkBeforeUpdatingReview);
This is the function to check auth before updating.
checkBeforeUpdatingReview: function(req, res) { // auth before updating review (can't use session or cookies)
console.log(req.body)
DB
.User
.findOne(
{
where : {
email: req.body.email,
},
}
)
.then (function (results) {
if (results[0] != '') {
if (bcrypt.compareSync(req.body.password, results.password)) {
return module.exports.updateReview(req, res, results)
} else { // same as catch
return res.render('reviews/edit/', { // i'm missing the ID (which i have in req.params.id) at the end of the route
id : req.params.id,
errors : "Incorrect username or password",
email : req.body.email,
});
}
}
})
.catch (function (error) {
console.log(error)
return res.render('reviews/edit/', { // i'm missing the ID (which i have in req.params.id) at the end of the route
id : req.params.id,
errors : "An unexpected error happened",
email : req.body.email,
});
})
},
If everything's ok, as seen above, it goes directly to this function
updateReview: function(req, res, results) { // update review
console.log(req.body)
DB
.Review
.update(req.body,
{
where : {
id: req.body.review_id,
}
}
)
.then(function (results) {
return res.redirect('/series/detail/' + req.body.series_id)
})
.catch (error => {
return res.send(error)
})
},
TL;DR: If auth fails, should go back to the review url and send the data that was sent so that the user does not lose it.
So that's it, if I could use sessions/cookies I think I would be able to go back to the last route, but I can't for this.
Thanks in advance!
I'm new to the MEAN stack app and am having some trouble trying to send data from the server to the front end. However, I do have some communication going on, but this is all I can seem to do. In the server I have the json message being sent.
Server
router.route("/users/register").post((req, res) => {
registerLogin.findOne({ $or: [{ username }, { email }]}, (err, user) => {
if (err)
res.send(err);
else if (!username || !email || !password)
res.status(403).json({ registerErrRequired: "Fill out whole form." });
Front end
registerUser(username, email, password) {
const user = {
username: username,
email: email,
password: password
};
return this.http.post(`${this.uri}/users/register`, user)
.pipe(map(response => console.log(response)))
.subscribe(() => { this.router.navigate(["/users/login"]) }, (errResp) => {
errResp.error ? this.ngFlashMessageService.showFlashMessage({
messages: [errResp.error.registerErrRequired], // Where json message gets caught and shown to the browser
dismissible: true,
timeout: false,
type: 'danger'
}) : console.log("An unkown error occured.");
});
}
This works well, but I can't seem to do req/res other than using a flash message. My issue is wanting to use it in other ways than just flash messages. For example, if the user does not have a session, then I want them to navigate back to the the log in page. Here's what I tried but failed.
Server
// Middleware
const redirectLogin = ((req, res, next) => {
if (!req.session.user)
res.status(401).json({ loggedIn: false });
else
next();
});
// Route
router.route("/home").get(redirectLogin, (req, res) => {
Blog.find((err, docs) => {
if (err)
console.log(err);
else
res.json(docs);
});
});
Front end
homePage() {
// Here is where I would like to say, If session, then true, else navigate(["/users/login"])
if (loggedIn === false)
this.router.navigate(["/users/login"])
else
// Success
return this.http.get(`${this.uri}/home`);
}
The only way I found communication was through sending error flash messages, but nothing else.
What you can do is call an api to check whether the user is logged in or not in ngOnInit lifecycle hook,so every time your component loads you can check whether the session exists on backend and route accordingly.
export class App implements OnInit{
constructor(){
//called first time before the ngOnInit()
}
ngOnInit(){
//CheckLogin() is a method in your service which calls your backend api
this.http.post("your authentication url to check if session exits ",{username:username}).subscribe(data=>{
if (data["loggedIn"] === false)
this.router.navigate(["/users/login"])
})
}
}
Angular also has HTTP interceptors,you can solve this issue with jwt and http interceptors
I got this error when I make subsequent request using mwbot on node.
response:
{ login:
{ result: 'Aborted',
reason: 'Cannot log in when using MediaWiki\\Session\\BotPasswordSessionProvider sessions' } } }
I am reading pages from mediawiki by providing a title. I thought that every request would need to login to read, but it seemed that I was wrong because this error seemed to complain that I already have logged in. But I don't know how the session can be read or how to find out that I already logged in or not.
the route:
router.get('/wikipage/:title', function(req, res, next) {
let title = req.params.title;
const MWBot = require('mwbot');
const wikiHost = "https://wiki.domain.com";
let bot = new MWBot();
let pageContent = "wiki page not created yet, please create";
bot.login({
apiUrl: wikiHost + "/api.php",
username: "xxx#apiuser",
password: "xxxxx"
}).then((response) => {
console.log("logged in");
return bot.read(title);
}).then((response) => {
for(let prop in response.query.pages) {
pageContent = response.query.pages[prop]['revisions'][0]['*'];
console.log("pageContent:", pageContent);
break;
}
res.json({
data: pageContent
});
}).catch((err) => {
// Could not login
console.log("error", err);
});
});
module.exports = router;
I presume you are running this in a browser, in which case the browser takes care of session cookie handling. You can check it the usual way via document.cookie.
While learning Sailsjs I am going through an example Chat application code. But it seems after successful login or signup the functions in MainController to do these are sending the entire User object found in db or created in db to the client side by the res.send(user) line.
Am I correct here? Is it not wrong and insecure to send password?
Or It is just not sent? If so, how?
login action from the api/controllers/MainController.js:
login: function (req, res) {
var username = req.param('username');
var password = req.param('password');
// Users.findByUsername(username)...
// In v0.9.0 the find method returns an empty array when no results are found
// when only one result is needed use findOne.
Users.findOneByUsername(username)
.done(function loginfindUser(err, usr){
if (err) {
// We set an error header here,
// which we access in the views an display in the alert call.
res.set('error', 'DB Error');
// The error object sent below is converted to JSON
res.send(500, { error: "DB Error" });
} else {
if (usr) {
var hasher = require("password-hash");
if (hasher.verify(password, usr.password)) {
req.session.user = usr;
res.send(usr);
} else {
// Set the error header
res.set('error', 'Wrong Password');
res.send(400, { error: "Wrong Password" });
}
} else {
res.set('error', 'User not Found');
res.send(404, { error: "User not Found"});
}
}
});
},
add a toJSON function in your Users model's attributes that removes the password
module.exports = {
attributes: {
...
toJSON: function() {
user = this.toObject()
delete user.password
return user
}
}
}
if you're properly encrypting your user's passwords, sending them down to the client isn't horrible, but it is still regarded as a bad practice.