So I have a very typical query.
I am new to this and I am building a simple login web app using node and express-session which are stored in MongoDB.
Here is my code:
sessionRouter.post("", async(req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
if (user) {
const sessionUser = sessionizeUser(user);
req.session.user = sessionUser;
console.log(req.session.id);
res.send(req.session.user);
} else {
throw new Error("invalid details.");
}
} catch (e) {
res.status(400).send(e);
}
});
//route to logout
sessionRouter.delete("", ({
session
}, res) => {
try {
const user = session.user;
if (user) {
console.log(session.id);
session.destroy((err) => {
if (err) console.log(err);
res.clearCookie(process.env.SESS_NAME);
res.send(session.id);
});
} else {
throw new Error("Something went wrong");
}
} catch (err) {
console.log("things went wrong!");
//res.status(422).send(JSON.stringify(err));
}
});
I am storing a 'user' attribute to req.session when I call the login API but when I call the logout API it generates a totally new session!.
Things go smoothly when I use postman to call these endpoints but when using a browser nothing works.
These are the calling functions I am using in browser:
const loggerin = () => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Connection", "keep-alive");
var raw = JSON.stringify({
email: "xxxxxxxxxx",
password: "xxxxxx",
});
var requestOptions = {
method: "POST",
headers: myHeaders,
body: raw,
redirect: "follow",
};
fetch("http://localhost:3001/api/session", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.log("error", error));
};
const loggerout = () => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Connection", "keep-alive");
var requestOptions = {
method: "DELETE",
};
fetch("http://localhost:3001/api/session", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.log("error", error));
};
const test = () => {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Connection", "keep-alive");
var requestOptions = {
method: "GET",
};
fetch("http://localhost:3001/api/session", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.log("error", error));
};
Please help!!! Thanks in advance!
The issue was that I was creating the session in the wrong function. I corrected that and it worked.
Related
This is my Frontend code
const fetchData = () => {
const options = {
method: 'GET',
url: 'http://localhost:1337/user/chart',
headers: {'x-access-token': sessionStorage.getItem('token')},
body: [chartData.datasets]
}
axios.request(options).then((response) => {
console.log(response)
}).catch((error) => {
console.error(error)})
}
This is backend
app.get('/user/chart', async (req, res) => {
const token = req.headers['x-access-token']
if (!token){
return res.status(404).json({ success: false, msg: "Token not found" });
}
try {
const decoded = jwt.verify(token, process.env.access_secret)
const email = decoded.email
await User.updateOne(
{ email: email },
{ $set: {} },
)
console.log(req.body)
return res.status(200).json({message: 'ok', label:[]})
} catch (error) {
console.log(error)
res.json({ status: 'error', error: 'invalid token' })
}
})
When I console.log(req.body) it is an empty {}.
Why is it empty?
I am using a GET request to retrieve the chart data
Axios API does not accept body on get get request you can send parameters with params example
const url = '/user/chart';
const config = {
headers: {'x-access-token': sessionStorage.getItem('token')},
params:{someKey:chartData.datasets}
};
axios.get(url, config)
Axios doesn't support setting a body for a get request, see the docs or this related question.
Though, I'd also recommend to reconsider your design. Typically the body isn't used in a GET request. If you're sending data to the server, you likely want to use POST or PUT instead. If you just want to pass a parameter, then you likely want to use request parameters.
If you absolutely need to send a body in your GET request, then you'll need to use a different tool.
frondend //
const fetchData = () => {
const options = {
method: 'POST',
url: 'http://localhost:1337/user/chart',
headers: {'x-access-token': sessionStorage.getItem('token')},
body: {name : "xx",mail:"xx#"}
}
axios.request(options).then((response) => {
console.log(response)
}).catch((error) => {
console.error(error)})
}
backend //
app.post('/user/chart', async (req, res) => {
const {name , mail} = req.body
const token = req.headers['x-access-token']
if (!token){
return res.status(404).json({ success: false, msg: "Token not found" });
}
try {
const decoded = jwt.verify(token, process.env.access_secret)
const email = decoded.email
await User.updateOne(
{ email: email },
{ $set: {} },
)
console.log(req.body)
return res.status(200).json({message: 'ok', label:[]})
} catch (error) {
console.log(error)
res.json({ status: 'error', error: 'invalid token' })
}
})Ï
I didn't initially get this error, but it mysteriously appeared later on in my code. I tried following allong with the firebase documentation and using the auth.getAuth() method but then got the following error:
** : TypeError: auth.getAuth(...).verifyIdToken is not a function **
This is my auth code:
//const { auth } = require('firebase-admin');
const { admin, db } = require('./admin');
//1. ******************
//const auth = require('firebase/auth');
module.exports = (req, res, next) => {
let idToken;
if(req.headers.authorization && req.headers.authorization.startsWith('Bearer ')){
idToken = req.headers.authorization.split('Bearer')[1]
//returns an array of 2 strings --> 2nd element is the token
}
else{
console.error("NO TOKEN FOUND");
return res.status(403).json({error: "Unauthorized"});
}
admin.auth().verifyIdToken(idToken)
.then(decodedToken => {
req.user = decodedToken;
console.log(decodedToken);
return db.collection('users')
.where('userId', '==', req.user.uid)
.limit(1)
.get();
})
.then(data => {
req.user.handle = data.docs[0].data().handle;
return next();
})
.catch(err => {
console.error('Error while verifying token', err);
return res.status(403).json(err);
})
}
** where admin is require('firebase-admin');
I tried applying the auth.getAuth() method in the documentation, but my postman response says it is not a function. I'm very lost right now, any help would be very appreciated.
This is the full error:
{ "code": "auth/argument-error",
"message": "Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token."
}
Here is my users.js that the id token gets to:
const { admin, db } = require("../util/admin");
const config = require('../util/config')
const firebase = require('firebase/app')
firebase.initializeApp(config)
const { validateSignUpData, validateLoginData, reduceUserDetails} = require('../util/validators')
const auth = require('firebase/auth');
// const { user } = require("firebase-functions/v1/auth");
exports.signup = (req,res) => {
const newUser = {
email:req.body.email,
password:req.body.password,
confirmPassword:req.body.confirmPassword,
handle:req.body.handle,
};
const { valid, errors } = validateSignUpData(newUser);
if(!valid) return res.status(400).json(errors);
//when a user signs up give them a blank image
const noImg = 'blankpic.png';
//declare an errors object as an empty object
let token, userId;
db.doc(`/users/${newUser.handle}`).get()
.then((doc) => {
if(doc.exists){
return res.status(400).json({ handle: 'this handle is already taken'})
} else{
return auth.createUserWithEmailAndPassword(auth.getAuth(), newUser.email,newUser.password)
}
})
.then((data) => {
//return access token for user to req more data
//return data.user.getIdToken();
userId = data.user.uid;
return data.user.getIdToken();
})
.then((idToken) => {
token = idToken;
//create user document
const userCredentials = {
handle: newUser.handle,
email: newUser.email,
createdAt: new Date().toISOString(),
imageUrl: `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${noImg}?alt=media`,
userId
};
//persist document into users cllection
return db.doc(`/users/${newUser.handle}`).set(userCredentials);
//return res.status(201).json( { token } );
})
.then(() => {
return res.status(201).json({ token })
})
.catch((err) => {
console.error(err);
if (err.code == 'auth/email-already-in-use') {
return res.status(400).json({ email: 'Email is already in use'})
}
return res.status(500).json({ general: "Something went wrong with server; please try again"});
});
}
exports.login = (req,res) => {
const user = {
email: req.body.email,
password: req.body.password
};
const { valid, errors } = validateLoginData(user);
if(!valid) return res.status(400).json(errors);
auth.signInWithEmailAndPassword(auth.getAuth(), user.email, user.password)
.then(data => {
return data.user.getIdToken();
})
.then(token => {
return res.json({ token });
})
.catch((err) => {
console.error(err);
return res
.status(403).json({general: 'Wrong credentials, please try again'});
});
}
exports.addUserDetails = (req,res) => {
let userDetails = reduceUserDetails(req.body);
db.doc(`/users/${req.user.handle}`).update(userDetails)
.then(() => {
return res.json({ message: 'Details add successfully'});
})
.catch(err => {
console.error(err);
return res.status(500).json({error: err.code});
})
}
exports.getAuthenticatedUser = (req,res) => {
let userData = {};
db.doc(`/users/${req.user.handle}`).get()
.then(doc => {
if(doc.exists){
userData.credentials = doc.data();
return db.collection('likes').where('userHandle', '==', req.user.handle).get()
}
})
.then(data => {
userData.likes = [];
data.forEach(doc => {
userData.likes.push(doc.data());
});
//return res.json(userData);
return db.collection('notifications').where('recipient', '=='. req.user.handle).get();
// .orderBy('createdAt').limit(10).get();
})
.then(data => {
userData.notifications = [];
data.forEach(doc => {
userData.notifications.push({
//recipient: doc.data().recipient,
sender: doc.data().sender,
createdAt: doc.data().createdAt,
screamId: doc.data().screamId,
type: doc.data().type,
read: doc.data().read,
notificationId: doc.id
});
});
return res.json(userData);
})
.catch(err => {
console.error(err);
return res.status(500).json({ error: err.code });
})
}
exports.getUserDetails = (req, res) => {
let userData = {};
db.doc(`/users/${req.params.handle}`)
.get()
.then((doc) => {
if (doc.exists) {
userData.user = doc.data();
return db
.collection("critech")
.where("userHandle", "==", req.params.handle)
//.orderBy("createdAt", "desc")
.get();
} else {
return res.status(404).json({ errror: "User not found" });
}
})
.then((data) => {
userData.posts = [];
data.forEach((doc) => {
userData.posts.push({
body: doc.data().body,
createdAt: doc.data().createdAt,
userHandle: doc.data().userHandle,
userImage: doc.data().userImage,
likeCount: doc.data().likeCount,
commentCount: doc.data().commentCount,
screamId: doc.id,
});
});
return res.json(userData);
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};
exports.uploadImage = (req, res) => {
const busboyCons = require("busboy");
const path = require("path");
const os = require("os");
const fs = require("fs");
var busboy = busboyCons({ headers: req.headers });
let imageToBeUploaded = {};
let imageFileName;
// String for image token
//let generatedToken = uuid();
busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
console.log(fieldname, filename, mimetype);
// if (mimetype !== "image/jpeg" && mimetype !== "image/png") {
// return res.status(400).json({ error: "Wrong file type submitted" });
// }
// my.image.png => ['my', 'image', 'png']
filename = filename.toString();
const imageExtension = filename.split(".")[filename.split(".").length - 1];
// 32756238461724837.png
imageFileName = `${Math.round(
Math.random() * 1000000000000
).toString()}.${imageExtension}`;
//tmdir() --> temporarydirectory
const filepath = path.join(os.tmpdir(), imageFileName);
imageToBeUploaded = { filepath, mimetype };
//use file system lib to create the file object using the node js function file.pipe
file.pipe(fs.createWriteStream(filepath));
});
busboy.on("finish", () => {
admin
.storage()
.bucket()
.upload(imageToBeUploaded.filepath, {
resumable: false,
metadata: {
metadata: {
contentType: imageToBeUploaded.mimetype,
//Generate token to be appended to imageUrl
// firebaseStorageDownloadTokens: generatedToken,
},
},
})
.then(() => {
//construct image url to add to our user
// Append token to url
const imageUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}`;
return db.doc(`/users/${req.user.handle}`).update({ imageUrl });
})
.then(() => {
return res.json({ message: "image uploaded successfully" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: "something went wrong" });
});
});
busboy.end(req.rawBody);
};
exports.markNotificationsRead = (req, res) => {
let batch = db.batch();
req.body.forEach((notificationId) => {
const notification = db.doc(`/notifications/${notificationId}`);
batch.update(notification, { read: true });
});
batch
.commit()
.then(() => {
return res.json({ message: "Notifications marked read" });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};
This is the token I get back when I log in 2
The response I get back when I use the token in my authorization header in Postman 1
The authorization header is of format Bearer <Token> with a space in between. However you are passing 'Bearer' in split() which would result in ['', ' <token>'] (notice the additional whitespace before actual token). You must use .split(" ") and this should resolve it. Try refactoring the code as shown below:
if( req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
idToken = req.headers.authorization.split(' ')[1]
}
const r1 = "Bearer token".split("Bearer")
const r2 = "Bearer token".split(" ")
console.log(`Token 1: '${r1[1]}'`)
console.log(`Token 2: '${r2[1]}'`)
I am getting an error to GET data from my API endpoint.
I am able to send data and also update/ delete them from the postTodo()method.
I have added it in a useEffect()so that the I am able to send data to server whenever a Todo is completed or deleted.
But whenever i reload the page, in the devtools, the todos array is [].
Some help would be appreciated.Thanks.
The Todo.jsx
const postTodo = (todos) => {
console.log(todos);
axios.post("http://localhost:4000/api/todos", todos, {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
}
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})
}
useEffect(() => {
postTodo(todos)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [todos])
useEffect(() => {
axios.get("http://localhost:4000/api/todos", {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
}
})
.then(res => {
console.log(res);
setTodos(res.data.todos)
})
.catch(err => {
console.log(err);
})
}, [])
the server.js
const authCheck = (req, res, next) => {
if (req.headers['authorization']) {
const token = req.headers['authorization'].split(" ")
if (token[0] !== 'Bearer') {
return res.send({ status: 'error', error: 'invalid request' });
} else {
req.jwt = jwt.verify(token[1], process.env.jwtSECRET);
return next();
}
}
}
app.post("/api/todos", authCheck, async (req, res) => {
const todos = req.body
console.log(todos);
const { id } = req.jwt
const user = await User.findByIdAndUpdate(id, { "todos": todos })
// console.log(user);
})
app.get("/api/todos", authCheck, async (req, res) => {
const { id } = req.jwt
const user = await User.findById(id)
log(user) //user.todos is empty
res.send({
status: "ok", todos: user.todos })
})
You can try something like this, where use effect for todos will log the value everytime you create a new todo
const postTodo = (todos) => {
console.log(todos);
axios.post("http://localhost:4000/api/todos", todos, {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
}
})
.then(res => {
console.log(res);
getTodos()
})
.catch(err => {
console.log(err);
})
}
const getTodos = () => {
axios.get("http://localhost:4000/api/todos", {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
}
})
.then(res => {
console.log(res);
setTodos(res.data.todos)
})
.catch(err => {
console.log(err);
})
}
const newTodo = () => {
const allTodos = [...todos];
allTodos.push("new Todo at:" + new Date())
postTodo(allTodos)
}
useEffect(() => {
console.log('todo-list', todos)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [todos])
useEffect(() => {
getTodos()
}, [])
return (<button onClick={() => }> Add Todo </button>)
The problem was solved,
actually it was the useEffect() issue.
I removed the UseEffect
and added the postTodos method after every useState hook updation.
I would like to be able to redirect from registration-page to login-page on successfull registration and again from login-page to home-page afteer successfull login.
I dont know what methods to use or where to call them.
This is the register call.
app.post("/api/register", async (req, res) => {
const { username, password: plainTextPassword } = req.body;
const password = await bcrypt.hash(plainTextPassword, 10);
try {
const response = await User.create({
username,
password
})
console.log("User created", response)
} catch (error) {
if (error.code === 11000) {
return res.json({ status: "error", error: "Username already in use" })
}
throw error
}
res.json({ status: "ok" });
});
This is the script
<script>
const form = document.getElementById("reg-form");
form.addEventListener("submit", registerUser);
async function registerUser(event) {
event.preventDefault();
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
const result = await fetch("/api/register", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username,
password
})
}).then((res) => res.json())
if (result.status === "ok") {
alert("Success");
} else {
alert(result.error)
}
}
</script>
</body>
</html>
You should return the line that redirects
return res.redirect('/UserHomePage');
Axios Post request
// Create profile
export const createProfile = (profileData, avatar, history) => dispatch => {
dispatch(clearErrors());
const image = new FormData();
image.append("avatar", avatar, avatar.name);
axios
.post("/api/profile", image, profileData)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
Edit ---> Axios post request second attempt
// Create profile
export const createProfile = (profileData, avatar, history) => dispatch => {
dispatch(clearErrors());
const image = new FormData();
image.append("avatar", avatar, avatar.name);
image.append("user", profileData, profileData.username);
axios
.post("/api/profile", image)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
profileData is what i want in the req.body and avatar is what i receive in req.file in my back-end with multer, but what i receive is the req.file with the image but nothing in my req.body(Just an empty object)
This is my router in node
router.post(
"/",
upload.single("avatar"),
passport.authenticate("jwt", { session: false }),
(req, res) => {
console.log(req.body);
}
);
Try to implement in following way using FormData
handleSubmit(e)
{
e.preventDefault();
const err = this.validate();
if (!err) {
var formData = {
category: this.state.category,
course: this.state.course,
};
const { category, course } = this.state;
let fd = new FormData();
fd.append('Test', this.state.testFile, this.state.testFile.name);
fd.append('category', category);
fd.append('course', course);
console.log(fd);
axios({
method: 'post',
url: 'http://localhost:7777/api/uploadTest',
data: fd,
})
.then((response) => {
if (response.data == 'Success') {
alert('Test has been Added..!!');
}
else {
alert('Something went wrong');
this.setState({ category: '' });
}
// this.setState({success:'Alert: '+response.data});
})
.catch((e) => {
console.error(e);
this.setState({ success: 'Alert: Something went wrong' });
});
}
}
I consider your route as /api/profile in route file.
You don't show your header profileData.
It should be like this
const profileData = {
headers: { 'content-type': 'multipart/form-data' }
}
Then you can request to the server as you already did.