I am using Knex JS for user authentication in order to get email and password from the user and connect to PostgreSQL to check for authentication.
router.post('/login', async (req, res) => {
knex.select('email','password').from('users')
.where('email', '=',req.body.email)
.then((data) => {
const isValid = bcrypt.compareSync(req.body.password, data[0].password);
if (isValid === true) {
res.render('index-v1');
}
});
});
But the render function is not rendering the index ejs file but rather the localhost is not responding.
Thanks in advance for the help.
So, as the comments suggest, there are two possible paths not covered by your route, which is apparently leading to a lack of response from the server. Remember, if you don't tell it to respond (with res.render or similar) it won't respond, leaving your client hanging.
router.post('/login', async (req, res) => {
try {
const data = await knex.select('email', 'password')
.from('users')
.where('email', '=', req.body.email)
const isValid = bcrypt.compareSync(req.body.password, data[0].password);
if (isValid) {
res.render('index-v1');
return
}
res.render('error-page');
} catch (e) {
res.render('error-page');
}
});
In other words: if the password is incorrect, we still need to respond to the client. If there's some kind of database error (or the user doesn't exist, say) we still need to respond to the client. Exactly how you respond is of course up to you, but that's the kind of structure you need to think about.
Related
I am working on my first full stack application, specifically with the MERN stack, and have run into a bit of an issue. I am trying to implement a leaderboard stored in a db for a unity game on my website. I have everything working wherein the client can post and get scores from my MongoDB Atlas database using my Express api. However, in the case of a leaderboard, I need to insure that the scores can ONLY be sent by the client based on how the game goes. With the current working configuration, anyone can send spoof scores via the api without having to play the game.
I first thought was to try to implement JWT to authenticate that the api call was coming from the site, but in my head any auth token like JWT could still be copied down by a user and sent with spoofed scores easily with Postman.
I am not extensively familiar with databases and suspect that this could possibly be solved if I wasn't using a DBaaS provider like Atlas but I am not entire sure.
Any thoughts or recommendations would be greatly appreciated!
You could define a middleware function and check the method of the incoming request:
const allowOnlyPost = (req, res, next) => {
if (req.method !== 'POST') {
return res.status(401).send(`Method ${req.method} not allowed`)
}
next()
}
module.exports = { allowOnlyPost }
And then apply it to the routes you want to protect:
const { allowOnlyPost } = require('./your/middleware/folder')
app.use('/route/to/protect', allowOnlyPost, (req, res) => { ... })
An improvement to current answer's function could be:
const allowMethods = (...methods) => {
return (req, res, next) => {
if (!methods.map(m => m.toUpperCase()).includes(req.method.toUpperCase())) {
return res.status(401).send(`Method ${req.method} not allowed`)
}
next()
}
}
module.exports = { allowMethods }
So you could use it like this:
const { allowMethods } = require('./your/middleware/folder')
app.use('/route/to/protect', allowMethods('get','post'), (req, res) => { ... })
Let's say I have the following Next.js api route.
/api/protected-api
This api will get a authorization: "Bearer: TOKEN" header for authorization purposes.
import { NextApiHandler } from "next";
const apiHandler: NextApiHandler = async (req, res) => {
await runAuthMiddleware(req,res);
// THE REST OF THE API LOGIC SHOULD ONLY RUN IF AUTHORIZATION SUCCEEDS
// IN THEORY, runAuthMiddleware WOULD RESPOND WITH 403 IF AUTHORIZATION FAILS
return res.json(data);
};
What I mean by the code above is:
If authorization fails, I would like to respond a 403 from the runAuthMiddleware function, and don't even bother running the rest of the code in the apiHandler.
Is this even possible? Is this an anti-pattern?
Should I got with something like this instead?
const apiHandler: NextApiHandler = async (req, res) => {
const authSuccess = await runAuthMiddleware(req,res);
if (authSuccess)
return res.json(data);
else
return res.status(403).send("Forbidden");
};
UPDATE:
It seems that there's no easy way of doing it. This package offers a possible solution: next-api-middleware
There is an easy way to do it if that is the only thing that you need, just make your own higher order function which checks auth:
const withAuth = (f) => async (req, res) => {
const isLogged = await checkIfUserIsLogged(req)
if (isLogged) {
return f(req, res)
} else {
return res.status(403).send("Forbidden");
}
}
const apiHandler = withAuth(async (req, res) => {
// This code now only runs of the user is logged
return res.json(data);
});
For more complex scenarios I would recommend to use https://github.com/hoangvvo/next-connect or something like that. But if you only need one middleware then it is completely fine to write your own.
I am working on API Get request. I have created a POST request to add the data in firebase realtime database. The code is as follows:
// CREATE POST
app.post("/post", (req, res) => {
let key;
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in.
var newPost = firebase.database().ref("posts/");
var myPost = newPost.push({
createdBy: user.uid,
from: req.body.from,
to: req.body.to,
duration: req.body.duration,
comments: req.body.comments,
});
res.send(newPost);
const postId = myPost.key;
console.log(postId);
} else {
// No user is signed in.
res.status(404).send("No user is signed in right now!");
}
});
});
Now, in order to get a specific post, I have written the following code:
// GET SPECIFIC POST
app.get("/post/:id", (req, res) => {
let response;
firebase
.database()
.ref("posts/" + req.params.id)
.on("value", (snapshot) => {
response = snapshot.val();
});
res.send(response);
});
I am new at Firebase, so I dont really know how to get a specific post. Please help me out
Calls to Firebase are asynchronous, because they require a call to the server. While that call is happening, your main code continues. And then when the data is available, your callback is invoked with the data from the server.
Right now your res.send(response) runs before the response = snapshot.val() is ever called. The rule with asynchronous APIs is simple: any code that needs the data needs to be inside the callback, or be called from there.
So in your case:
app.get("/post/:id", (req, res) => {
firebase
.database()
.ref("posts/" + req.params.id)
.once("value")
.then((snapshot) => {
res.send(snapshot.val());
});
});
You'll note that I also change from on to once, since you only care about getting the value once (instead of attaching a permanent listener that monitors the database for changes).
Dealing with asynchronous API is a common stumbling block, so I recommend spending some time reading these answers to learn more:
Why Does Firebase Lose Reference outside the once() Function?
Firebase response is too slow
Best way to retrieve Firebase data and return it, or an alternative way
I simply did this:
app.get("/post/:id", (req, res) => {
var key = req.params.id;
console.log(key);
firebase
.database()
.ref("posts")
.child(key)
.get()
.then((snapshot) => {
res.send(snapshot.val());
});
});
this solved the problem
I am trying to create signup and Login for the first time with express and react using PostgreSQL. My post works just fine. A user can be added to the database so I jumped into handling duplicates.
I am using the findUserByEmail function to find my email and then, in my routes, create the user if it does not exist.
I tried everything and still is giving me problems. I manage to get it working by just returning the query, without a response, which I don't think is right:
const findUserByEmail = (req, response) => {
return pool.query("SELECT * FROM users WHERE email = $1", [req.body.email])
};
Although, I need the response to handle the errors.
The way that I found more common and is how I am trying is:
const findUserByEmail = (req, response) => {
pool.query("SELECT * FROM users WHERE email = $1", [req.body.email]),
(error, results) => {
if (error) {
throw error;
}
response.json(results.rows);
};
};
And when I call it here:
app.post("/signup/user", (req, res, next) => {
queries
.findUserByEmail(req, res)
.then(user => {
if (user.rows.length > 0) {
res.status(400).send("this email is already in use");
} else {
queries.createUser(req.body, res);
}
})
.catch(err => {
console.log(err);
res.status(500).send("Something went wrong");
});
});
But the error is:
Cannot read property 'then' of undefined
If anybody can give me a hand cause I've been 2/3 weeks just for the authentication.
I'll leave my repo if anybody wants to have a look, is a bit messy though.
https://github.com/jaitone/CRUD-in-JS
Thank you!
if you are using pg as part of your project. then:
const findUserByEmail = (req, response) => { // send just email instead
return pool.query("SELECT * FROM users WHERE email = $1", [req.body.email])
};
Is completely legal and beautiful. The library creates a promise and returns it.
I manage to get it working by just returning the query
It is not returning the query, it is returning the mechanism to run the query in a promise wrapper(to be run in the future). So when you do .then it will actually execute and return the result. BUT
If you want to do it manually:
In the findUserByEmail you are not returning a Promise, instead you are just ending the request chain by saying res.json(which in turn means you are returning undefined).
You can create a Promise wrapper or use util.promisfy to make the pool.query a promise.
const findUserByEmail = (req, response) => { // send just email instead
return new Promise((resolve, reject)=>{
pool.query("SELECT * FROM users WHERE email = $1", [req.body.email]),
(error, results) => {
if (error) {
reject(error);
}
resolve(results.rows);
};
});
};
Note, sending the email instead of whole req and res objects is a good idea.
The Problem
I deployed a create-react-app webapp to aws ec2. It's used to display data from a database and send data to it. I use ExpressJS, CORS and MySQL.
With the following code i fetch the corresponding URL and the server.js sends back the database content. Until here, everything works fine.
getBets = _ => {
fetch("http://ec2***.amazonaws.com
.then(response => response.json())
.then(response => this.setState({bets: response.data}))
.catch(err => console.error(err))
};
The problem begins when sending data to the database with the following code:
addBet = _ => {
const { bet } = this.state;
fetch(`http://ec2***.amazonaws.com/bets/add?name=${bet.person_name}&bet=${bet.time_bet}`)
.then(response => response.json())
.then(this.getBets)
.catch(err => console.error(err))
};
On click the addBet-function populates the db, but in chrome I following error:
GET http://ec2***.amazonaws.com/bets/add?name=Peter%20Pan5&bet=10:17%205 net::ERR_EMPTY_RESPONSE
and
TypeError: Failed to fetch
Regarding chrome dev-tools, the first error corresponds to the fetch in the addBet function and the second error to the catch part.
On the server side I've the following code for processing the fetch:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES("${name}", "${bet}", CURTIME())`;
connection.query(INSERT_BET, (err, res) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I want to mention, that the res paramter in the app.get part is unused. That tells me my IDE.
After a lot of hours digging deeper in the topics of expressJS and the fetch api, I guess, that the app.get part doesn't send a response to the server. But the fetch need some response.
My Question
How do I have to change the code in the app.get part to send a proper response back to the server?
AND
Am I right with my guess?
In MYSQL when you do an insert query you get back err,results and fields in the callback function like this:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function (error,
results, fields) {
if (error) throw error;
console.log(results.insertId);
});
You have used the parameter res for result and then you have used res.send() which now corresponds to that res parameter in the callback function and not the res object.Rewrite it like this:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES(?,?,?)`;
connection.query(INSERT_BET,[name,bet,CURTIME()] ,(err, result) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I have also used prepared statement in place of normal sql queries. These are used to prevent sql injections. I hope it will work now.