The connexion is working but when i tried to get information from my DB with the middleware on, i have an error 401. For information, the request works on postman but not on my app.
I think the problem comes from the authentification bearer in my fetch
my middleware :
/* Middleware */
const authentification = (req, res, next) => {
try {
/* decode token and compare, set userID */
const token = req.headers.authorization.split(" ")[1];
const decodedToken = jwt.verify(token, "RANDOM_TOKEN_SECRET");
const userId = decodedToken.userId;
/* if userID in body compare with DB userID, else (no userID in body) it's ok*/
if (req.body.userId && req.body.userId !== userId) {
throw "Invalid user ID";
} else {
User.findOne({ _id: userId }, (err, data) => {
if (err) {
res.status(500).json({
error: new Error("Internal server error"),
});
return;
}
if (!data) {
res.status(404).json({
message: "Erreur d'authentification",
});
return;
}
req.user = data;
next();
});
}
} catch {
res.status(401).json({
message: "Invalid request!",
});
}
};
And my fetch
getClubUser = async () => {
const headers = new Headers({
'Content-type': 'application/json',
Authorization: 'bearer' + (await AsyncStorage.getItem('token')),
});
const options = {
method: 'GET',
headers: headers,
};
fetch('http://localhost:8080/dashboard/clubInfo', options)
.then((response) => {
console.log(response);
return response.json();
})
.then(
(data) => {
this.setState({sport: data});
console.log(this.state.sport.clubs);
},
(err) => {
console.log(err);
},
);
};
Related
I get this error when I try and send a POST request to the server:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:174:12)
at ServerResponse.json (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:278:15)
at D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\src\api\endpoints\account\create.js:35:36
at D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\mongoose\lib\model.js:5214:18
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
code: 'ERR_HTTP_HEADERS_SENT'
}
The router and routes are working fine it's just the endpoint.
It will respond to the client with {code:"EMAIL_TAKEN"} which is expected but then it also tries to send the USERNAME_TAKEN thing
I have tried moving parts of the code into separate files and using different status codes.
Code:
module.exports = async (req, res) => {
const schema = require("../../models/account");
const username = req.headers.username;
const email = req.headers.email;
if(!username) {
return res.status(400).json({ "code": "NO_USERNAME" });
}
if(!email) {
return res.status(400).json({ "code": "NO_EMAIL" });
}
if(username.length > 20) {
return res.status(400).json({ "code": "USERNAME_TOO_LONG" });
}
schema.findOne({ username }, async (err, data) => {
if(err) {
return res.status(500);
}
if(data) {
return res.status(400).json({ "code": "USERNAME_TAKEN" });
}
})
schema.findOne({ email }, async (err, data) => {
if(err) {
return res.status(500);
}
if(data) {
return res.status(400).json({ "code": "EMAIL_TAKEN" });
}
})
schema.findOne({ username, email }, async (err, data) => {
if(err) {
return res.status(500);
}
if(!data) {
data = new schema({
username,
email
})
await data.save();
return res.status(200).json({ "code": "CREATED_ACCOUNT" });
}
})
}
Using return in the findOne() callbacks won't stop the other schema.findOne() calls from being made. I'd recommend awaiting on Mongoose's promise API instead to keep everything nice and flat
module.exports = async (req, res) => {
const schema = require("../../models/account");
// headers? 🤔
const username = req.headers.username;
const email = req.headers.email;
if (!username) {
return res.status(400).json({ code: "NO_USERNAME" });
}
if (!email) {
return res.status(400).json({ code: "NO_EMAIL" });
}
if (username.length > 20) {
return res.status(400).json({ code: "USERNAME_TOO_LONG" });
}
try {
if (await schema.exists({ username })) {
return res.status(409).json({ code: "USERNAME_TAKEN" });
}
if (await schema.exists({ email })) {
return res.status(409).json({ code: "EMAIL_TAKEN" });
}
const data = new schema({
username,
email,
});
await data.save();
return res.status(201).json({ code: "CREATED_ACCOUNT" });
} catch (err) {
console.error(err);
res.sendStatus(500);
}
};
On my comment about sending your username and email values via headers... typically you would send them via a POST request in the request body.
For example, client-side...
fetch("/accounts", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ username, email }),
})
and server-side...
const { username, email } = req.body;
You just need to include the appropriate request body middleware, eg
app.use(express.json());
I'm getting this error -> " Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client ".I tried to apply JSON Web Token(JWT) in my app and every time it's logging me out from app. I can't understand is it server-side error or client-side. Pls check my code
Here is the code:
// verifyJWT function
function verifyJWT(req, res, next){
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).send({ message: 'Your access is unauthorized to BookPile' });
}
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.ACCESS_TOKEN_SECRECT, (err, decoded) => {
if (err) {
return res.status(403).send({ message: 'BookPile authority forbid your access' })
}
console.log('decoded', decoded);
req.decoded = decoded;
next();
})
console.log('inside verify function', authHeader);
next();
}
//API
// GET API for get token
app.post('/login' , async(req,res) => {
const user = req.body;
const accessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, {
expiresIn:'1d'
});
res.send({accessToken});
})
// GET API for load items of logged in user
app.get('/my_items' , verifyJWT , async(req,res) => {
// const authHeader = req.headers.authorization;
// console.log(authHeader);
const email = req.query.email;
const criteria = {email:email};
const cursor = await booksCollection.find(criteria);
const books = await cursor.toArray();
res.send(books);
})
//Client side
const getMyBooks = async () => {
const email = user.email;
const url = `http://localhost:5000/my_items?email=${email}`;
try {
const { data } = await axios.get(url, {
headers: {
authorization: `Bearer ${localStorage.getItem('accessToken')}`
}
})
setMyBooks(data);
}
catch (error) {
console.log((error.message));
if (error.response.status === 401 || error.response.status === 403) {
signOut(auth);
// navigate('/login');
}
}
}
getMyBooks();
}, [user])
//Client side login
const handleForm = async event => {
event.preventDefault();
const email = emailRef.current.value;
const password = passRef.current.value;
await signInWithEmailAndPassword(email, password);
const { data } = await axios.post('http://localhost:5000/login', { email });
// console.log(data);
localStorage.setItem('accessToken', data.accessToken);
navigate(from, { replace: true });
}
Since the verify function is executed asynchronously, the last next() call will be called immediately before the verification is done. Therefore you'll have to call either res.send() or next() inside the callback
function verifyJWT(req, res, next){
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).send({ message: 'Your access is unauthorized to BookPile' });
}
const token = authHeader.split(' ')[1];
jwt.verify(token, process.env.ACCESS_TOKEN_SECRECT, (err, decoded) => {
if (err) {
return res.status(403).send({ message: 'BookPile authority forbid your access' })
}
console.log('decoded', decoded);
req.decoded = decoded;
next();
})
}
I've created a route called login and another one function(middleware), if the authentication is good, this can be redirected to the delete route but unfortunately, my jwt token is not recognizable.
I want to know why, the problem must be in this part of api :
Api
function parseArticle (req, res, next) {
let token = req.headers['x-access-token'] || req.headers['authorization'];
if (!!token && token.startsWith('Bearer ')) {
token = token.slice(7, token.length);
}
if (token) {
jwt.verify(token, 'RANDOM_TOKEN_SECRET', (err, decoded) => {
if (err) {
return res.status(401).json('token_not_valid');
} else {
req.decoded = decoded;
const expiresIn = 24 * 60 * 60;
const newToken = jwt.sign({
user : decoded.user
},
'RANDOM_TOKEN_SECRET',
{
expiresIn: expiresIn
});
next();
}
});
} else {
return res.status(401).json('token_required');
}
}
router.route('/article/:articleId')
.get(parseArticle, (req, res) => {
db.query("SELECT * FROM articles WHERE id_article = (?)", [req.articleId], function (err,result) { if (err) throw err;
console.log(result);
res.json(result)
})
})
.put(parseArticle, (req, res) => {
const name = req.body.name
const description = req.body.description
const image = req.body.image
const price = parseInt(req.body.price)
req.article.name = name
req.article.description = description
req.article.image = image
req.article.price = price
res.send()
})
.delete(parseArticle, (req, res) => {
db.query("DELETE FROM articles WHERE id_article = (?)", [req.articleId], function (err,result) { if (err) throw err;
console.log(result);})
res.json({message: "Propre"})})
router.post('/login', (req, res) => {
function runQuery(sqlQuery, args){
return new Promise(function (resolve, reject) {
db.query(sqlQuery, args, function(error, results, fields) {
if (error) reject(error);
else resolve(results);
});
});
}
runQuery("SELECT * from users where email = ? ", [req.body.email]).then(user => {
if (user.length === 0) {
return res.status(401).json({ error: 'Utilisateur non trouvé !',
success : 0});
}
bcrypt.compare(req.body.password, user[0].password)
.then(valid => {
if (!valid) {
return res.status(401).json({ error: 'Mot de passe incorrect !',
success : 0});
}
const userId = user[0].id_user;
const token = jwt.sign({ userId: user[0].id_user },
'RANDOM_TOKEN_SECRET',
{ expiresIn: '24h' });
res.header("Authorization","Bearer " + token)
return res.status(200).json({success : 1})
})
.catch(error => res.status(500).json({ error }));
})
.catch(error => res.status(500).json({ error }));
});
Login.vue
<template>
<form #submit.prevent="checkData">
<input type="text" name="email" v-model="login.email" placeholder="Entrez votre adresse-mail"/>
<input type="text" name="password" v-model="login.password" placeholder="Entrez votre mot de passe"/>
<button type="submit"> Connexion </button>
</form>
</template>
<script>
module.exports = {
name: "Login",
data () {
return {
login : {
email: "",
password: "",
},
}
},
methods : {
async checkData() {
let user = {email : this.login.email, password : this.login.password}
try {
const response = await axios.post('/api/login', user)
console.log(response.data.success)
if(response.data.success === 1){
await this.$router.push({name: 'home'})
}
if(response.data.success === 0){
this.error = "Connexion Échouée"
}
}
catch(err) {console.error("network error",err)}
}
}
}
</script>
<style scoped>
button {
padding: 10px;
}
</style>
Thanks for you help,
Have a nice week-end
You return the token to the client in the Authorization header:
res.header("Authorization","Bearer " + token)
This is a non-standard use of this header, it will not automatically be sent back in the next request. At the least, you would need extra code on the client to receive this header and store the token, for example, in the session storage.
It's probably easier to store the token in a session cookie, which will be handled automatically by the client:
res.cookie("token", token, {httpOnly: true})
Also consider the secure option for the cookie. You must also extend your server-side code to find the token in the cookie:
let token = req.headers['x-access-token'] || req.headers['authorization']
|| req.cookies.token;
I'm trying to sign up new user, when I'm sending the post request the server register the user well, and I can see them in my data base, but I can't see the success log in my console (I can catch the error and it logs in my console).
Server side code:
var express = require("express");
const { Error } = require("mongoose");
const passport = require("passport");
var router = express.Router();
const User = require("../models/user");
const catchAsync = require("../utils/catchAsync");
router.post(
"/register",
catchAsync(async (req, res) => {
try {
const { email, username, password } = req.body;
const user = new User({ email, username });
await User.register(user, password);
} catch (e) {
throw new Error("Error signing up");
}
})
);
module.exports = router;
Client side code:
const sumbitHandler = async (data) => {
const { username, email, password } = data;
try {
await fetch("http://localhost:9000/users/register", {
method: "POST",
body: JSON.stringify({
username,
email,
password,
}),
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
if (res && !res.ok) {
throw new Error("ERROR");
}
console.log("Success");
})
.catch((e) => {
console.log(e.message);
});
} catch (e) {
console.log(e.message);
}
};
You are mixing the async/await style and the older .then() Promise-style. Choose one or the other (I strongly recommend async/await)
You are not transforming fetch's response into JSON, leaving it in Promise state.
Your server is never responding to the client! You need to add res.end(), res.send(), res.json() or something.
const sumbitHandler = async (data) => {
const { username, email, password } = data;
try {
const response = await fetch("http://localhost:9000/users/register", {...});
const serverResponse = await response.text(); // or response.json() if your servers sends JSON back
console.log("Success! serverResponse is = ", serverResponse ); // "Done!"
} catch (e) {
console.log(e.message);
}
};
Server :
...
await User.register(user, password);
res.send("Done!"); // or res.json({ status : "ok" }); etc.
I'm doing requests to my API server to authenticate a user, that's not the problem. The problem is that I don't know why my async function doesn't return anything, and I get an error because the data that I want from this function is undefined.
Don't worry if the error management is ugly and in general I can do this better, I'll do that after fixing this problem.
Utils.js class
async Auth(username, password) {
const body = {
username: username,
password: password
};
let req_uuid = '';
await this.setupUUID()
.then((uuid) => {
req_uuid = uuid;
})
.catch((e) => {
console.error(e);
});
let jwtData = {
"req_uuid": req_uuid,
"origin": "launcher",
"scope": "ec_auth"
};
console.log(req_uuid);
let jwtToken = jwt.sign(jwtData, 'lulz');
await fetch('http://api.myapi.cc/authenticate', {
method: 'POST',
headers: { "Content-Type": "application/json", "identify": jwtToken },
body: JSON.stringify(body),
})
.then((res) => {
// console.log(res);
// If the status is OK (200) get the json data of the response containing the token and return it
if (res.status == 200) {
res.json()
.then((data) => {
return Promise.resolve(data);
});
// If the response status is 401 return an error containing the error code and message
} else if (res.status == 401) {
res.json()
.then((data) => {
console.log(data.message);
});
throw ({ code: 401, msg: 'Wrong username or password' });
// If the response status is 400 (Bad Request) display unknown error message (this sould never happen)
} else if (res.status == 400) {
throw ({ code: 400, msg: 'Unknown error, contact support for help. \nError code: 400' });
}
})
// If there's an error with the fetch request itself then display a dialog box with the error message
.catch((error) => {
// If it's a "normal" error, so it has a code, don't put inside a new error object
if(error.code) {
return Promise.reject(error);
} else {
return Promise.reject({ code: 'critical', msg: error });
}
});
}
Main.js file
utils.Auth('user123', 'admin')
.then((res) => {
console.log(res); // undefined
});
Your Async function must return the last promise:
return fetch('http://api.myapi.cc/authenticate', ...);
or await the result and return it:
var x = await fetch('http://api.myapi.cc/authenticate', ...);
// do something with x and...
return x;
Notice that you don’t need to mix promise syntax (.then) with await. You can, but you don’t need to, and probably shouldn’t.
These two functions do exactly the same thing:
function a() {
return functionReturningPromise().then(function (result) {
return result + 1;
});
}
async function b() {
return (await functionReturningPromise()) + 1;
}
await is not to be used with then.
let data = await this.setupUUID();
or
let data=null;
setupUUID().then(res=> data = res)
I would try something like this:
const postReq = async (jwtToken) => {
const body = {
username: username,
password: password,
};
try {
const res = await fetch('http://api.myapi.cc/authenticate', {
method: 'POST',
headers: { "Content-Type": "application/json", "identify": jwtToken },
body: JSON.stringify(body),
})
if (res) {
if (res.status == 200) {
return res.json();
} else if (res.status == 401) {
const data = res.json();
console.log(data.message)
throw ({ code: 401, msg: 'Wrong username or password' });
} else if (res.status == 400) {
throw ({ code: 400, msg: 'Unknown error, contact support for help. \nError code: 400' });
}
}
} catch (err) {
console.error(err)
}
};
const Auth = async (username, password) => {
const jwtData = {
"origin": "launcher",
"scope": "ec_auth"
};
try {
const req_uuid = await this.setupUUID();
if (req_uuid) {
jwtData["req_uuid"] = req_uuid;
const jwtToken = jwt.sign(jwtData, 'lulz');
return await postReq(jwtToken);
}
} catch (err) {
console.error(err);
};
}