testing authenticated express endpoint with superagent does not work? - node.js

I am testing my application however I could not figure out how to test the authenticated endpoints with supertest, based on documentation i wrote this, but it is not working. Can someone help me how to test authenticated endpoints: this is my code:
const request = require("supertest");
const app = require("../../server/app");
let testSession = null;
beforeEach(function () {
testSession = request.agent(app);
});
test("Should add new blogs", async () => {
await testSession
.post("/api/v1/blogs")
.send({
title: "first test",
location: "ny",
position: "developer",
})
.expect(200);
});
here is the result:
expected 200 "OK", got 401 "Unauthorized"
endpoints are working but here are the controller and route:
exports.createBlog = (req, res) => {
//key comes from client
//because if other user send the request I dont wanna lock the request for other one
//we send the key with the save button in the controller menu
const lockId = req.query.lockId;
let blog;
if (!lock.isBusy(lockId)) {
lock
.acquire(lockId, async () => {
const blogData = req.body;
blog = await new Blog(blogData);
if (req.user) {
blog.userId = req.user.sub;
blog.author = req.user.name;
}
await blog.save();
})
.then(() =>
res.status(200).json({ message: "Blog is saved!", blog: blog })
)
.catch((e) => res.status(422).json({ message: e.message }));
} else {
return res.status(422).json({ message: "Blog is saving" });
}
router.post(
"/",
authService.checkJwt,
authService.checkRole("siteOwner"),
blogCtrl.createBlog
);

Related

Node Express server not send messages with whatsapp-web-js

i have a problem with my custom server, i'm trying to setup an api that send whatsapp messages in react as a frontend.
So, i have actually a route that send the QR to the frontend (working fine), a route that handle the authentication event (working fine) and
PROBLEM HERE:
...a route that send a message to a specific number (NOT WORK)
here my server code...what i'm doing wrong? if i launch a POST request to the endpoint on postman, i get an infinite loading (no errors).
const express = require('express')
const router = express.Router()
const fs = require('fs')
const { Client, LocalAuth } = require('whatsapp-web.js')
const authStrategy = new LocalAuth({
clientId: 'adminSession',
})
const worker = `${authStrategy.dataPath}/session-admin/Default/Service Worker`
if (fs.existsSync(worker)) {
fs.rmdirSync(worker, { recursive: true })
}
const client = new Client({
takeoverOnConflict: true,
authStrategy,
})
const sessionData = {
client: 'admin',
session: true,
qrCodeScanned: true,
}
client.on('authenticated', (session) => {
fs.writeFile(
'waSession.json',
JSON.stringify(sessionData),
'utf-8',
(err) => {
if (!err) {
console.log('Session saved on disk...')
}
}
)
})
router.get('/whatsapp/auth', async (req, res) => {
const dir = './waSession.json'
fs.readFile(dir, (err, data) => {
if (data.length === 0) {
return res.status(200).json({
message: 'You need to login first',
})
} else {
return res.status(200).json({
message: 'You are logged in.',
})
}
})
})
router.get('/whatsapp', async (req, res) => {
try {
client.on('qr', (qr) => {
res.status(200).send({
message: 'Connect whatsapp with this qr-code',
qrCode: qr,
})
})
await client.initialize()
res.status(404)
} catch (err) {
res.send(err)
}
})
router.post('/whatsapp/send', async (req, res) => {
const { phoneNumber, message } = req.body
try {
client.on('ready', async () => {
const number = phoneNumber
const text = message
const chatId = number.substring(1) + '#c.us'
await client.sendMessage(chatId, text)
})
await client.initialize()
res.json('Messaggio inviato')
} catch (err) {
res.status(404).send(err)
await client.destroy()
}
})
module.exports = router
A Client represents one authenticated WhatsApp user, and you have only one global variable client. This implies that all incoming requests will represent the same WhatsApp user, even if several different real users send requests to your server in parallel. This is probably not what you intend.
I suggest that you use express-session to associate every client with a session. Then a user needs to create a client and authenticate it only once during a session. All subsequent requests in the same session will re-use that client, and the client.on(...) and client.initialize() commands will not be repeated.

(ReactJS) Getting error in Login Component, while Register Compo. works perfectly

I can't seem to get why my Login component is getting an error when my Registration component works well while using the same POST request to the backend server. The only thing that they differ is the method of retrieving data from MongoDB in their backend script partner, which is what I am thinking is the problem, but anything I do doesn't seem to work.
Edit > * The error in the Login Component is AxiosError: Network Error. Both the Login and Register backend have been tested in Postman and works well, and responds a status. So it seems that the problem is in the Login React Component's Axios post request. It send data to the backend okay, but it catches an error after that.*
The login script of the backend server is working well and validating the credentials perfectly. But then, React gets an error.
in Login React Component (AxiosError):
async postReq() {
const loginData = JSON.stringify(
{
'email': this.state.email,
'password': this.state.password,
},
);
console.log(loginData)
let validation = await axios.post(
'http://localhost:5000/login',
loginData,
{ headers: {'Content-Type':'application/json'}
})
.then((res) => {
console.log(`Login successful. ${res}`);
let response = res;
this.props.redirect('/session');
})
.catch((error) => {
console.log(error);
console.log(`Cannot login. ${error.message}`)
console.log(error.request);
let response = error;
alert("Damn.")
});
}
in Register React Component (works smoothly):
handleSubmit() {
// POST to server
const regData = JSON.stringify(
{
'firstname': this.state.fname,
'lastname': this.state.lname,
'email': this.state.email,
'birthday': this.state.birthday,
'password': this.state.password,
'country': this.state.country,
'city': this.state.city,
'provstate': this.state.provstate,
'contactnum': this.state.contactnum,
'formpicture': this.state.img,
'disclcond': this.state.cond,
},
);
console.log(regData)
axios.post(
'http://localhost:5000/register',
regData,
{ headers: {'Content-Type':'application/json'}
})
.then((res) => {
console.log(`Registered successfully. ${res}`);
setTimeout(() => this.props.redirect('/login'), 2000)
})
.catch((res) => {
console.log(`Not registered. ${res}`)
alert("Damn.")
});
}
NodeJS, Mongoose || Login backend:
const router = require('express').Router();
let User = require('../db_models/user.model');
router.route('/').get((req, res) => {
res.sendStatus(200);
res.end();
})
// If user submits login credentials, check database
router.route('/').post((req, res) => {
const email = req.body.email;
console.log(email)
const password = req.body.password;
let accountMatched = null;
async function checkPassword() {
await User.findOne({ 'email' : email })
.then(user => {
if (user.password === password) {
console.log(`true ${user.email} :: ${user.password}`);
accountMatched = true;
res.sendStatus(200);
} else {
console.log(`damn!! ${err}`)
res.sendStatus(404);
throw err
}
})
.catch(err => console.log(err))
accountMatched === true ? console.log('Passed') : res.send('Failed');
res.end()
}
checkPassword();
})
module.exports = router;
Register backend:
const router = require('express').Router();
let User = require('../db_models/user.model');
router.route('/').get((req, res) => {
res.send('hello hello');
res.end();
})
// If user submits registration credentials, submit to database
router.route('/').post((req, res) => {
console.log(req.body)
const firstname = req.body.firstname;
const lastname = req.body.lastname;
const email = req.body.email;
const birthday = Date.parse(req.body.birthday);
const password = req.body.password;
const contactnum = req.body.contactnum;
const country = req.body.country;
const city = req.body.city;
const provstate = req.body.provstate;
// below only pass links
const formpicture = req.body.formpicture;
const disclcond = req.body.disclcond;
const newUser = new User({
firstname,
lastname,
email,
birthday,
password,
country,
city,
provstate,
contactnum,
formpicture,
disclcond,
});
newUser.save()
.then(() => {
console.log('User added.');
res.sendStatus(200);
res.end();
})
.catch(err => {
console.log(`Damn, user not added. ${err}`);
res.end();
})
});
module.exports = router;
I would really appreciate some help.
Try exchange the Login Component part to something like this if you wanna use async/await.
async postReq() {
const loginData = JSON.stringify(
{
'email': this.state.email,
'password': this.state.password,
},
);
console.log(loginData)
try {
let res = await axios.post(
'http://localhost:5000/login',
loginData,
{ headers: {'Content-Type':'application/json'}
})
console.log(`Login successful. ${res}`);
let response = res;
this.props.redirect('/session');
} catch (error) {
console.log(error);
console.log(`Cannot login. ${error.message}`)
console.log(error.request);
let response = error;
alert("Damn.")
}
}

"Bad request" error when using params in supertest and jest

I have the following controller for updating project which gets id from param.
const updateProject = async (req: Request, res: Response, next: NextFunction) => {
const { title, desc } = req.body;
const { projectID } = req.params;
const { user } = <any>req;
if (!isValidObjectId(projectID)) {
return next(new ErrorResponse("Invalid project ID", 400));
}
try {
const project = await Project.findOneAndUpdate(
{ _id: projectID, user: user._id },
{
title: title,
desc: desc,
},
{ new: true }
);
if (!project) {
return next(new ErrorResponse("Project not found", 404));
}
res.status(200).json(project);
} catch (err) {
next(err);
}
};
I've written a test for this controller but I keep getting "Bad Request". But when I test it manually using Postman, it works fine. And other tests that do not require params work fine too.
import mongoose from "mongoose";
const projectOneId = new mongoose.Types.ObjectId();
export const projectOne = {
_id: projectOneId,
title: "First Project",
desc: "This is the first project",
user: userOneId,
};
describe("/api/projects", () => {
//login using cookie before running the test
let cookie: string;
beforeEach(async () => {
await request(app)
.post("/api/auth/login")
.send(userOne)
.expect(200)
.then((res) => {
const cookies = res.headers["set-cookie"][0]
.split(",")
.map((item: string) => item.split(";")[0]);
cookie = cookies.join(";");
});
});
describe("PUT /api/projects/:projectID", () => {
it("Should update a project", async () => {
await request(app)
.put(`/api/projects/:projectID`)
.set("Cookie", cookie)
.query({ projectID: projectOne._id.toString() })
.send(projectOne)
.expect(200);
});
});

How to use JWT with stripe to retrieve a paymentIntent?

I have a MEVN stack application that uses JWT for auth and that can take stripe payments.
Upon payment, I need to retrieve the payment intent object for that charge and send it to the front end to validate payment and serve up a PDF. My Question is, how can I make sure that the customer only had access to the charge created by that particular user by using the Json web token.
My current node.js code for stripe (without JWT)
const express = require("express");
const router = express.Router();
const endpointSecret = process.env.WEBHOOK_SECRET;
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
let Intent;
router.post("/", async (req, res) => {
const session = await stripe.checkout.sessions.create(
{
success_url: "http://localhost:8080/#/success",
cancel_url: "http://localhost:8080/#/cancel",
payment_method_types: ["card"],
line_items: [
{
price: "price_1H0up7Kc91wTjOOikyrKImZs",
quantity: 1,
},
],
mode: "payment",
},
function (err, session) {
if (err) {
console.log(err);
res.status(500).send({ success: false, reason: "session didnt work" });
} else {
console.log(session);
Intent = session.payment_intent;
console.log(Intent);
res.json({ session_id: session.id });
// res.status(200).send({ success: true });
}
}
);
});
router.get("/confirm", async (req, res) => {
const intentObject = await stripe.paymentIntents.retrieve(Intent, function (
err,
paymentIntent
) {
if (err) {
console.log(err);
res
.status(500)
.send({ success: false, reason: "cannot retrieve payment" });
} else {
console.log(paymentIntent);
res.status(200).json({ status: paymentIntent.status });
setTimeout(() => (intent = ""), 10);
}
});
});
module.exports = router;
You can't use Intent the way you're using it there; it won't persist between requests.
You might want to consider using something like this: https://github.com/auth0/express-jwt

access the info of the user that created the post

I am making a react-native mobile app and I am having trouble passing the users info that created the post to the home page in the post detail. I can pass the userID but for some reason when I add the rest of the info to the payload I can't create a post. Please help.
BACKEND
This is the requireAuth file that requires authentication before performing a tast. My code for the user is here as well at the bottom---
const mongoose = require("mongoose");
const User = mongoose.model("User");
module.exports = (req, res, next) => {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).send({ error: "You must be logged in." });
}
const token = authorization.replace("Bearer ", "");
jwt.verify(token, "mySecretKey", async (err, payload) => {
if (err) {
return res.status(401).send({ error: "You must be logged in." });
}
const { userId, name, phone, email } = payload;
const user = await User.findById(userId);
req.user = user;
console.log(req.user);
next();
});
};
This is the POST route for the Item---
router.post("/items", requireAuth, async (req, res) => {
const { title, category, detail, condition, price } = req.body;
if (!title || !category || !detail || !condition || !price) {
return res.status(422).send({
error: "You must provide a title, category, detail, condition, and price"
});
}
try {
const item = new Item({
title,
category,
detail,
condition,
price,
userId: req.user._id
});
await item.save();
res.send(item);
} catch (err) {
res.status(422).send({ error: err.message });
}
});
FRONT-END
This is my createItem function in the itemContext file---
const createItem = dispatch => async ({
title,
category,
detail,
condition,
price
}) => {
try {
const response = await sellerApi.post("/items", {
title,
category,
detail,
condition,
price
});
//this is the other place the error might be happening i need this to save in the phone local storage
dispatch({ type: "create_item", payload: response.data });
navigate("Home");
} catch (err) {
console.log(err);
}
};
All I am trying to do it is when the post is being displayed so is the info of the post creator
For existing post in the database: If you are referencing your user in post model like this
const Post = mongoose.model('Post', {
// other fields
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
then you can use populate to fetch user of that post.
const post= await Post.findById('5c2e505a3253e18a43e612e6')
await post.populate('userId').execPopulate()
console.log(post.userId)

Resources