postman (thunder client) can send request but axios cannot - node.js

I am trying to send http requests using axios to my node backend. For some reason, axios keeps returning a 500 (Internal Server Error) even when thunder client (dollar store version of postman) is able to send the request and get a proper response.
index.js (server)
app.get('/api/login', async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email })
if(user===undefined) { res.status(404).json("user not found"); }
const validPassword = await bcrypt.compare(req.body.password, user.password)
!validPassword && res.status(400).json("wrong password")
res.status(200).json(user)
} catch (err) {
res.status(500).json(err)
}
})
Login.js (frontend)
const login = (email, password) => {
console.log(email + ': ' + password)
axios.get('http://localhost:8800/api/login', { email: email, password: password })
.then((response) => console.log(response))
.catch((err) => console.log(err.response))
}
err.response returns no useful data and err.response.data is a blank object. I've tried to edit the request header, but it is already 'application/json'. Again, this request works on thunder client and I made sure that the data I passed in was correct through the console.log(email + ': ' + password . I've been trying to fix this issue for hours so please help. Thank you in advance.
Update: I had previously binded the login function to an onClick to a button, but I put the axios function directly into the brackets instead of login(email, password). The issue persists.
Second Update: I followed the comments' advice and console logged the error on the terminal. It returned TypeError: Cannot read properties of null (reading 'password'). This was strange because in the function, I had console logged password and it returned the proper text. It also says that it cannot find a user within my database that uses the email I am currently using, but even when I pass in the exact email I use in thunder client requests, I still get the error. I think the data is not getting there properly.
Third Update: My hypothesis is confirmed. In index.js, I made the route console log req.body.email and it returned undefined. I passed in an object that I JSON stringified and when console logged in the browser, it returns a proper object. The request is sending an object with undefined properties although I am passing in an object with values

In this case, the issue was that the request was a get request, not a post request. Get requests do not take in data while post requests do. Here is the fix:
index.js (server)
app.post('/api/login', async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email })
if(user===undefined) { res.status(404).json("user not found"); }
const validPassword = await bcrypt.compare(req.body.password, user.password)
!validPassword && res.status(400).json("wrong password")
res.status(200).json(user)
} catch (err) {
res.status(500).json(err)
}
})

If you have to receive the request parameters in body (mainly in json format) then you have to go with POST type request.
In the GET type request, you can get request parameters in the form of params and query string.
Parameters may be either part of path:
myapi/customers/123
or a query string:
myapi?customer=123
More details can be found here:
https://www.restapitutorial.com/lessons/httpmethods.html

Related

when building a fullstack project with express, how do I use "res.status(200).json( {some information...} )" in the front end?

I am learning about express servers, but I do not understand why all the tutorials I am watching are writing code like the following 3 lines when dealing with responses
res.status(200).json({user});
res.status(401).json("Wrong username");
res.status(500).json(err);
How does this help my home.html (or home.ejs) file??
for example, if I am building a login feature like:
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username });
if (!user) {
return res.status(401).json("Wrong credentials!");
}
const hashedPassword = CryptoJS.AES.decrypt(user.password, process.env.PASS_SEC);
const originalPassword = hashedPassword.toString(CryptoJS.enc.Utf8);
if (req.body.password !== originalPassword) {
return res.status(401).json("Wrong credientials!");
}
const accessToken = jwt.sign(
{id: user._id, isAdmin: user.isAdmin},
process.env.JWT_SEC,
{expiresIn: "3d"}
);
const { password, ...others } = user._doc;
res.status(200).json({...others, accessToken});
} catch (err) {
res.status(500).json(err);
}
});
How does those "res.status" lines actually let me do things on the front end. Like how do I access this information, e.g. to save the accessToken in a request header for my next request, or how do I manipulate the html file to say hi ${user.username} or something like that.
Or if an error occures, how do I make my application say "username is wrong" or "login failed" or whatever, because res.status() is NOT doing any of that let me tell you...
99% of the content I find online uses react or angular, I only use plain JS and HTML, any help is appricated because I have no clue where to look and I have enough with learing express and do not want to learn react
In an express server you can return responses in an intuitive way like below:
return res.status(200).send({
'message' : 'Okay'
});
The status codes explain the status of the request. Though we have freedom to use any status code that we want the general practice is that the we follow:
2xx - Okay (request successful)
4xx - An issue with the client end of the request - you can use this to throw out errors when something is wrong with the client whos sending the request , like the user credentials is wrong or the requested record does not exists etc.
5xx - An issue with the server like the db connection failed, or file write failed etc.
On the front end depending on the status code of the request you can change the UI. Also note that the message that you send via res.send({}) is also available in the front end which u can use to show the user of the front end.

Sending data from react to node by get request using axios library

for more than 3 hours I'm handling with an issue, what I'm trying to do is sending a get request from react to nodejs using axios library, I wanna pass some data into this request, as we know the get request don't have a body, so I've sent the data as query parameter like that
// base URL
const url = "http://localhost:8080/loginAsTeacher";
if(loginAs === "loginTeacher"){
axios.get(url,{
params :{
email: "abc123#gmail.com",
password: "abc1234*"
}
})
.then(res => console.log(res)) // this line return status:200 and data:null
.catch(err => console.log(err.message))
}
so this request success but the problem is the email and password are not passing to the backend
router.get("/loginAsTeacher", async (req,res)=>{
// values coming from the client
const loginEmail = req.params.email;
const loginPassword = req.params.password;
console.log(req.params); // this line return {} empty object
// get data of that user by his/her mail
const teacherData = await myModel.findOne({
email: loginEmail
}).exec()
res.status(200).json({
status: 200,
data: teacherData
})
})
the console.log above return an empty object, which means there's no parameters
Is this not the right solution ???
thanks for reading
To get your queries you need to get req.query instead of req.params
Btw. it's dangerous to send sensitive data over get. It could be get logged in plaintext even over https
use req.query instead of req.params. it will solve the issue

Why is 'currentUser' and 'onAuthStateChanged' in firebase always null?

What I want to achieve
A user, who logged in or signed up should not re-login after one hour. The restriction of one hour comes from firebase authentication, if not prevented (what I try to accomplish).
Problem
After a user is logged in via firebase authentication (signInWithEmailAndPassword) I always get null for currentUser and onAuthStateChanged.
What I tried
I'm using React (v17.0.2) using 'Create React App'. On server side I'm using NodeJS (v12). The communication between both is accomplished using axios (v0.21.1)
First I tried to send the token stored in localStorage, which came from firebase (server side), back to the server. But the server tells me, that the token is no longer valid. Server side code as follows:
module.exports = (request, response, next) => {
let idToken;
if (request.headers.authorization && request.headers.authorization.startsWith('Bearer ')) {
idToken = request.headers.authorization.split('Bearer ')[1];
console.log("idToken:", idToken);
} else {
console.error('No token found');
return response.status(403).json({ error: 'Unauthorized' });
}
admin
.auth()
.verifyIdToken(idToken)
.then((decodedToken) => {
console.log('decodedToken', decodedToken);
request.user = decodedToken;
return db.collection('users').where('userId', '==', request.user.uid).limit(1).get();
})
.catch((err) => {
console.error('Error while verifying token', err);
return response.status(403).json(err);
});
};
After that I tried the following code on client side.
handleSubmit = () => {
const userData = {
email: this.state.email,
password: this.state.password
};
axios
.post(firestoreUrl() + '/login', userData)
.then((resp) => {
console.log("token:", resp.data); //here I get a valid token
localStorage.setItem('AuthToken', `Bearer ${resp.data.token}`);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //but this is null
})
.then((data) => {
console.log(data);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //still null
})
.catch((error) => {
console.log("Error:", error);
});
};
What irritates me is that I get a token from firebase (server side), the token is then stored in localStorage (client side) but firebase then tells me, that the currentUser is null. But presumably they are not mutually dependent =/.
I'm able to access all secured sites in my app. I can log out and in again. But whatever I do the currentUser is null.
I also tried to run the code above in componentDidMount()-method. But no success.
I tried an approach from this link (hopefully in a way it should be), but it didn't work. Still getting null for both currentUser and onAuthStateChanged if I implement following code.
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log("state = definitely signed in")
}
else {
console.log("state = definitely signed out")
}
})
I always get logged to the console, that the user is 'definitely signed out'.
During research I noticed that the point at which I should try to get the currentUser-Status is kind of tricky. So I guess that one solution is to implement the currentUser-code at another/the right place. And here I'm struggling =/.
As I found out at a similar question here on SO, I did a bad mistake. Apparently, it's not a good idea to perform the signIn- or createUser-functionality on server side. This should be done on client side. In the question mentioned above are some good reasons for doing that on server side but in my case it's quite ok to run it on client side.
Thanks to Frank van Puffelen for leading the way (see one of the comments in the question mentioned above).

NODE/EXPRESS: [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I'm working on building an node/express backend and continue to receive the following error: (node:35061) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client. I'm not exactly sure what i am doing wrong here... Can someone educate me on what the issue might be? TIA!
Route
userRoutes.post('', async (req, res) => {
try {
const { email, password } = req.body;
const validate = await signUp.validate({ email, password });
res.send(validate);
const newUser = new User({ email, password });
const sessionUser = sessionizeUser(newUser);
await newUser.save();
req.session.user = sessionUser;
res.send(sessionUser);
return;
} catch (error) {
res.status(400).send(parseError(error));
}
});
The problem lies here in try block
try {
const { email, password } = req.body;
const validate = await signUp.validate({ email, password });
res.send(validate); //over here
const newUser = new User({ email, password });
const sessionUser = sessionizeUser(newUser);
await newUser.save();
req.session.user = sessionUser;
res.send(sessionUser); //over here
return;
}
this means is that for a given client request the server previously sent a response (either a success response with the resource requested or error response for a bad request) back to the client and now is unexpectedly trying to send another response
Solution
The simple fix for this error is to add javascript return statement to the response being sent from the if conditional to ensure that the request handler function exits(terminate) excuting code with the function once a response has being sent to the client.
The description for the return statement on MDN states
When a return statement is used in a function body, the execution of the function is stopped. If specified, a given value is returned to the function caller.

Simple Auth middleware with bcrypt is not functionning on postman

I am quite new in backend, and its my first time using a middleware with nodejs. Until now, my register function and format password are functionning on postman, since when I register a new user, I find on my DB a hashed password.
however, when I want to log in with the same user, I receive my message (wrong password or mail).
my console.log(user) read properly the user profil on node, but after, it seems like bcrypt.compareSync is not functionning. id my console.log(token), returns nothing.
I surely have a problem on my generateToken function or on my login function but I cannot figure it out.
you can see below my code.
login (req,res){
Users.find({mail:req.body.mail})
.then(users =>{
console.log(users);
if(users.length > 0 && bcrypt.compareSync(req.body.mail+req.body.password, users[0].password)){
const token= generateToken(users[0]);
console.log(token);
res.status(200).send('operation succeed: \n' + token);
//res.statut(200).redirect('/logged');
}
else{
res.status(500).send('wrong password or mail');
}
})
.catch(err =>{
res.send(err);
});
},
}
my generateToken function:
function generateToken(user){
const payload={
iat:moment().unix(),
exp:moment().add(14,'days').unix(),
iss:user.mail,
sub:user.password
}
return jsonwebtoken.sign(payload,'app_secret');
}
thank you for your help.

Resources