I have an issue regarding Axios in my React project, Nodejs. During my Post request my request arrives at my back end but i can't have the Axios response. I try to reply with Status Code but nothing comes to my front. For info, I have a proxy in the package.json of my Front.
Here the part in my Front:
await axios({
method: 'post',
url: "http://localhost:5000/api/user/register",
data: {
username,
email,
password,
},
proxy: {
protocol: 'http',
host: 'localhost',
port: 5000,
}}).then(function (res) {
console.log(res);
console.log(res.data);
console.log(res.errors);
})
}};
Here is my Back end file:
module.exports.signUp = async (req, res) => {
const { username, password, email } = req.body;
try {
const existUsername = await User.findOne({username: username});
if(existUsername){
res.status(404);
console.log('premiere partie du if');
} else {
console.log('je suis dans le else');
const user = new User({ username: username, email: email });
await user.setPassword(password);
await user.save();
res.status(200);
}
} catch (err) {
console.log(err);
res.redirect("/api/user/register");
}
};
I have no answer with Axios. So I can't handle my mistakes. If someone has an idea. Thank you
You need to put res.send() to send back an empty body, or else Express won't send anything back.
Related
I am trying to have my server reject the signup request if the user tries to sign up with an existing account. However, I cant seem to reject it properly and pass the error message to my client side.
//server.js
app.post('/signup', (req, res) => {
const email = req.body.email
const plainTextPassword = req.body.password;
//check if user already exists
User.find({ email: email }, (err, existingUser) => {
//account doesnt exist
if (existingUser.length === 0) {
bcrypt.hash(plainTextPassword, saltRounds, async (err, hash) => {
try {
const user = new User({
email: email,
password: hash
});
let result = await user.save();
if (result) {
res.send(result)
}
} catch (e) {
res.send(e);
}
})
} else {
//notify user that account exists
return Promise.reject(new Error('Account already exists'))
}
})
})
//reduxSlice.js
export const signup = createAsyncThunk(
'userAuth/signup',
async (payload, thunkAPI) => {
const { email, password } = payload
try {
const result = await fetch(
signupPath, {
mode: 'cors',
credentials: 'include',
method: "post",
body: JSON.stringify({ email, password }),
headers: {
'Content-Type': 'application/json'
}
}
)
return result.json()
} catch (error) {
console.log(error) //this line executes
}
}
)
From my reduxdev tools, my signup is still fulfilled EVEN though I rejected it from my server. Also, my server crashes after one attempt, which leads me to suspect there is an uncaught error.
The client only receives what you do res.send() with or next(err) which will then call res.send(). Promises are local only to the server and are not something that gets sent back to the client.
In your original code, I'd suggest that you use ONLY promise-based asynchronous operations and then you can throw in your code, have one place to catch all the errors and then send an error back to the client from there.
class ServerError extends Error {
constructor(msg, status) {
super(msg)
this.status = status;
}
}
app.post('/signup', (req, res) => {
try {
const email = req.body.email
const plainTextPassword = req.body.password;
//check if user already exists
const existingUser = await User.find({ email: email });
//account doesnt exist
if (existingUser.length !== 0) {
throw new ServerError('Account already exist', 403);
}
const hash = await bcrypt.hash(plainTextPassword, saltRounds);
const user = new User({
email: email,
password: hash
});
const result = await user.save();
res.send(result);
} catch (e) {
if (!e.status) e.status = 500;
console.log(e);
res.status(e.status).send({err: e.message});
}
});
Then, in your client code that is using fetch(), you need to check result.ok to see if you got a 2xx status back or not. fetch() only rejects if the network connection to the target host failed. If the connection succeeded, even if it returns an error status, the fetch() promise will resolve. You have to examine result.ok to see if you got a 2xx status or not.
//reduxSlice.js
export const signup = createAsyncThunk(
'userAuth/signup',
async (payload, thunkAPI) => {
const { email, password } = payload
try {
const result = await fetch(
signupPath, {
mode: 'cors',
credentials: 'include',
method: "post",
body: JSON.stringify({ email, password }),
headers: {
'Content-Type': 'application/json'
}
});
// check to see if we got a 2xx status success
if (!result.ok) {
throw new Error(`signup failed: ${response.status}`);
}
return result.json()
} catch (error) {
console.log(error) //this line executes
}
}
)
i´m creating a Authentication page with React and Express. I'm using JWT too.
I´ve made this route in the back:
server.js
...
app.use(
cookieSession({
name: "prode_session",
secret: "MIOURI_PRODE_SECRET", //add to .env variable
httpOnly: false,
})
);
app.use(cors());
...
auth.routes.js
app.post("/signin", controller.signin);
user.routes.js
app.get(
"/user",
[authJwt.verifyToken],
(req, res) => res.send(true)
)
auth.controller.js
exports.signin = async (req, res) => {
const user = await Users.findOne({
where: { email: req.body.email },
});
try {
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
const passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({
message: "Invalid Password!",
});
}
const token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 84000, //24hours
});
req.session.token = token;
console.log(req.session);
return res.status(200).send({
isLogged: true,
id: user.id,
email: user.email,
suscripcion: user.suscripcion,
preference_id: user.preference_id,
token,
});
} catch (error) {
console.log(error);
}
};
authJWT.js
verifyToken = async (req, res, next) => {
let token = req.session.token;
console.log(`THIS IS THE TOKEN: ${token}`);
if (!token) {
return res.status(403).send({
message: "No token provided",
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
console.log(err);
return res.status(401).send({
message: "Unauthorized!",
});
}
req.id = decoded.id;
next();
});
};
const authJwt = { verifyToken };
module.exports = authJwt;
When I test this with POSTMAN, it works Ok, I mean, if first I try to make the GET request, the response is "No token provided", but if I signin first, generate the token and then make the GET request, I get true.
The problem is when I try to implement this in the front.
I have this Login component in React in which I make a POST request with the credentials:
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch("http://localhost:3000/signin", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
email,
password,
}),
});
const data = await response.json();
console.log(data);
if (data.isLogged && data.suscripcion === true && data.token) {
await tokenAvailable()
//navigate(`/masthead/${email}&${data.isLogged}&${data.id}`);
} else if (data.isLogged && data.suscripcion === false) {
navigate("/suscripcion", {
state: { preference_id: data.preference_id },
});
} else {
window.alert("Invalid Login");
}
} catch (error) {
console.log(error);
}
};
async function tokenAvailable() {
const user = await fetch("http://localhost:3000/user", {
method: "GET",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
const response = await user.json();
setUser(await response);
console.log(await response);
return response;
}
When I make the POST, the GET request is executed (tokenAvailable function) after receiving the response, but I receive "No token Provided" while I expect to receive "true" as in Postman.
From what I debug, the authJWT.js file, is not receiving nothing from the req.session.token.
When I compare the headers from postman and the browser, in postan the SET-cookie key appears, but in the browser not.
postman:
browser:
I need some help here. I´ve been strugling with this for almost 3 days.
I found a solution for this. Apparently, the HttpOnly Cookie approach works if the React app and the back-end server hosted in same domain. So we need to use http-proxy-middleware for local development.
I´ve tried to install the http-proxy-middleware but a lot of errors came, so I decided to store de JWT in the localstorage.
I'm not sure how to check if the values match with the MongoDB data. I am using PUT and trying to use findOneAndUpdate to check if the values match.
<script>
const logindetails = new Vue({
el: '#logindetails',
data: {
email: "",
password: "",
on: Boolean
},
methods: {
login: function (e) {
e.preventDefault();
const log = {
email: this.email,
password: this.password,
}
const options = {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(log)
};
fetch('http://localhost:3000/users/${this.email}/${this.password}',
options).then(response => {
[...]
</script>
This is the server code (it successfully connected to MongoDB) :
app.put('/students/:email/:password', (req, res, next) => {
console.log("login");
res.setHeader("Content-Type", "application/json");
db.collection('users').findOne({email: (req.params.email), password: (req.params.password)},
{$set: {on: true}})
.then(results => res.send(results))
.catch(err => res.send(err))
});
I personally don't think it is a good idea to put your username and password as query string, because it hurts the restful api convention. It wouldn't make sense to use a put request if there is no body being pass. Also, a post request would make more sense in a login situation .Anyway I digress, here are the usual steps to doing authentication.
1. (Client-Side) Send the email and password in the body of the fetch request
//something like this
const body = { email, password };
const response = await fetch(
"http://localhost:5000/authentication/login",
{
method: "POST",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(body)
}
);
2.(Server-Side - make sure you to use app.use(express.json()) to access req.body)
//defining middleware to access req.body
app.use(express.json());
app.post("/authentication/login", async(req,res) =>{
//1. destructure email and password
const {email, password} = req.body
//2. check if user doesn't exist
const user = await db.user.find({user_email: email})
if(!user){
return res.status(401).send("User does not exist");
}
//3. Check if password is the same as the password in the database
if(password !== user.password){
return res.status(401).send("Wrong Credential")
}
//4. This is up to you when the user is authenticated
res.json(`Welcome back ${email}`);
})
I am struggling to make a login system using JSON web tokens.
I have made the login (client side) that calls to my server.js file.
This is the login through the client side Below is my handle submit function that calls the server.js login route.How would I use a token here?
handleSubmit(e) {
e.preventDefault();
if (this.state.email.length < 8 || this.state.password.length < 8) {
alert(`please enter the form correctly `);
} else {
const data = { email: this.state.email, password: this.state.password };
fetch("/login", {
method: "POST", // or 'PUT'
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(data => {
console.log("Success:", data);
})
.catch(error => {
console.error("Error:", error);
});
}
}
catch(e) {
console.log(e);
}
This is the login route for my server.js. As you can see I have assigned a jwt but how would I send this back to my login form and utilise it for protected routes.
app.post("/login", async (req, response) => {
try {
await sql.connect(config);
var request = new sql.Request();
var Email = req.body.email;
var Password = req.body.password;
console.log({ Email, Password });
request.input("Email", sql.VarChar, Email);
request.input("Password", sql.VarChar, Password);
const result = await request.execute("dbo.LoginUser");
if (result.recordsets[0].length > 0) {
console.info("/login: login successful..");
console.log(req.body);
const token = jwt.sign({ user: Email }, "SECRET_KEY", {
expiresIn: 3600000
});
var decoded = jwt.verify(token, "SECRET_KEY");
console.log(decoded);
response.status(200).json({
ok: true,
user: Email,
token: token
});
console.log(token);
} else {
console.info("/login: bad creds");
response.status(400).send("Incorrect email and/or Password!");
}
} catch (err) {
console.log("Err: ", err);
response.status(500).send("Check api console.log for the error");
}
});
Essentially all I want is for my submit handler to be called for login. Server returns a jwt token which can then be used to verify other routes.
There are two ways to route:
Use React-Redux and react-router.
Save the fetched JWT token into localStorage and use to validate route within your routes component.
I would recommend in using React-Redux / React-router for protected routing.
Here is a video link to Build Real Web App with React by
Rem Zolotykh
This will help you.
I am trying to send the data from input boxes in react server to nodejs server but everytime i am getting error on backend
TypeError: Cannot read property 'email' of undefined
Here is my code for that
onformsubmit=()=>{
console.log(this.state.email,this.state.password) ///gets printed correctly
axios.post('http://localhost:5000/acc-details',{
email:this.state.email,
password:this.state.password
})
.then(response=>{
console.log('success')
})
.catch(err=>console.log(err))
}
and then in node server
const express=require('express')
const app=express()
var bodyparser=require('body-parser')
app.use(bodyparser.json())
router.post('/acc-details',(req,res)=>{
console.log(req.body.email)
res.send('ok')
})
if not consoling in node server i am getting response back 'ok' as writtten above but i want to fetch my email and password on node server for db authentication
Modify your Axios request slightly to send multipart/form-data data.
onformsubmit = () => {
// Collect properties from the state
const {email, password} = this.state;
// Use FormData API
var formdata = new FormData();
formdata.append('email', email);
formdata.append('password', password);
axios.post('http://localhost:5000/acc-details', formdata)
.then( response=> {
console.log('success')
})
.catch(err=>console.log(err))
}
onformsubmit=()=>{
console.log(this.state.email,this.state.password) ///gets printed correctly
axios({
url: 'http://localhost:5000/acc-details'
method: 'POST',
data: { email: this.state.email, password: this.state.password }
})
.then(response=>{
console.log('success')
})
.catch(err=>console.log(err))
}
Now you should be able to access req.body
Edit:
after 200 tries, i figured out:
axios({
url: "http://localhost:5000/acc-details",
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"
},
data: { email: this.state.email, password: this.state.password }
});```