I have an API which will send an email to a user based off the input. This is the on lick submit. I can confirm the data is being console.log for the state.
const inviteUser = async () => {
userRequest.get(`companyprofile/companyUser/invite/${companyuser._id}`, teamMemberEmail);
console.log('invite sent to', (teamMemberEmail))
}
With this api, i send the body to the API and then email based off the text, however when i log the field it does not appear at all and only shows as {}
router.get("/companyUser/invite/:id", async (req, res) => {
// must update to company
var stringLength = 25;
const companyUser = await CompanyProfile.findById(req.params.id)
const companyUserToken = await companyUser.inviteToken
const companyAccessTokenStripped = await companyUserToken.substring(0, stringLength);
//encrypt the accesstoken to invite users
const url = `http://localhost:5000/api/auth/inviteToJoinTeam/${companyUserToken}/${req.body.email}`;
// const url = `http://localhost:5000/api/companyprofile/companyUser/inviteToJoinTeam/${companyUserToken}`;
console.log(req.body)
const mailOptions = {
from: 'company#gmail.com',
to: req.body.email,
subject: `You have been invited to join ${companyUser.CompanyTitle}`,
html: `${companyUser.companyTitle} has invited you to join their team ${url}`
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
try {
// const savedCompany = await newCompany.save();
res.status(201).json(companyUserToken);
} catch (err) {
res.status(500).json(err);
}
});
Can anyone see why i cannot pass this data and email the user? Appears to be an error with how it's passed but i can confirm that the endpoint works in postman if i just plug an email body in
You are trying to send an email whenever the GET route is called. However, the data won't be sent inside as "req.body" as a GET request doesn't have a body.
If you are using GET method then the data is sent as query parameters.
router.get("/companyUser/invite/:email", async (req, res, next) => {
console.log(req.params.email);
};
You can also either use a POST or PUT request in order to access the URL body
router.post("/companyUser/invite", async (req, res, next) => {
console.log(req.body.email);
};
Related
Getting error when calling sendVerificationCode.
Tech Stack Used : Nodejs:
async (req, res, next) => {
// #swagger.tags = ['Auth']
// #swagger.description = ' This endpoint allows to create a post'
const { phoneNumber, recaptchaToken } = req.body;
console.log(recaptchaToken);
const identityToolkit = google.identitytoolkit({
auth: process.env.GCP_API_KEY,
version: 'v3',
});
const response = await identityToolkit.relyingparty.sendVerificationCode({
phoneNumber,
recaptchaToken,
});
// save sessionInfo into db. You will need this to verify the SMS code
req.user.sessionInfo=response.data.sessionInfo;
await req.user.save();
res.status(200).json({success:true,data:"Otp sent"});
}
How to resolve this? I have tried every way I know.
i wrote a login page code in js that runs on node express.js server and anyone can put their username /email and password and all that data goes into an json file and it looks like this
{"username":"admin","password":"pasword","email":"user#stackoverflow.com","timestamp":1598668572045,"_id":"dx8HqKkVWe8olH5z"}
i managed to get the timestamp and NeDB gives a random _id to that object.
and when you login you go to a home page that looks like this
but the user gets the username value when there is on object only on the database which is "database.json"
if there is more than 1 object on the database that action will crash and the client can't see his name or any thing it shows nothing .
i don't know how to make it work with several objects on the database.
i thought JSON.parse Or stringfy could make it work but i don't know how to use them on my case.
so here is the js code
var jsonn = '/database.json';
async function userinfo() {
const response = await fetch(jsonn);
const data = await response.json();
var { username, password } = data;
document.getElementById('user').textContent = username;
console.log (data)
console.log (username);
console.log (id);
}
userinfo();
i appreciate any help, if you got any idea please share it with me i really need your help.
UPDATE :
the error message says :
uncaught (in promise) syntaxError : unxpected token in json positon 126.
my server.js code :
const Datastore = require('nedb');
app.listen(2000, () => console.log('listening at 3000'));
app.use(express.static('/public'));
app.use(express.json({
limit: '1mb'
}));
const database = new Datastore('public/database.json');
database.loadDatabase();
app.post('/public', (request, response) => {
const data = request.body;
const timestamp = Date.now();
data.timestamp = timestamp;
database.insert(data);
response.json(data);
console.log(data);
var logdate = new Date;
console.log(logdate);
});
There were some issues with the way that you are calling the DB insert. Basically, on every post request, you allow an insert. This is causing you to have multiple users with the same username. When you search for users by username then you will get a bunch of users. Instead, you want something like the sample code below.
I removed the status public to make it easier to test so make sure to add it back in so you can test front end code. Right now there's just a GET request endpoint so you can get the data by username through a query. This requires more cleanup and checks but at least it will get you started. Also, I remove the password and DB _id from the response as this is probs data you don't want to send back. Ideally, you will encrypt the password before storing it in DB.
const express = require('express');
const app = express();
const Datastore = require('nedb');
app.listen(3000, () => console.log('listening at 3000'));
app.use(express.json({
limit: '1mb'
}));
const database = new Datastore('public/database.json');
database.loadDatabase();
app.get('/public', (req, res) => {
const { username } = req.query;
database.findOne({
username,
}, (err, user) => {
if(err) {
return res.sendStatus(500);
}
delete user._id;
delete user.password;
return res.json(user);
});
});
app.post('/public', (req, res) => {
const data = req.body;
const {
username
} = data;
database.findOne({
username,
}, (err, user) => {
if(err) {
return res.sendStatus(500);
}
if(user) {
delete newUser._id;
delete newUser.password;
return res.json(user)
}
data.timestamp = Date.now();
database.insert(data, (createError, newUser) => {
if(createError) {
return res.sendStatus(500);
}
delete newUser._id;
delete newUser.password;
res.json(newUser);
});
});
});
I am using passport-jwt strategy, JWT in cookies to authenticate a user. The user authentication is working fine. However, at the endpoints where we need not check for authenticated user to grant access, I want to check is a user is logged in. If yes, I'll hide some options from the webpage and show others. I am trying to implement this by retrieving jwt from cookie, verifying it and finding the corresponding user and returning the user obtained.
Naturally, I want the user to be returned in User in the router before moving forward.
authenticate.js
var verifyToken = async function verifyToken(token, secretkey){
// console.log('This is token: ', token);
// console.log('This is secretkey: ', secretkey);
try{
var data = jwt.verify(token, secretkey);
return data;
} catch(err){
return err;
}
}
exports.loggedIn = async function loggedIn(req){
try{
var token = req.signedCookies.jwt;
console.log(token);
var userFound = false;
if(token){
data = await verifyToken(token, config.secretKey);
user = await getUser(data);
console.log('Data: ',data);
console.log('User: ', user);
}
else
return userFound;
}
catch(error){
console.log(err);
}
}
var getUser = function getUser(jwt_payload){
try{
var query = User.findOne({_id: jwt_payload._id});
var returnUser = query.exec();
return returnUser;
}
catch(err){
console.log(err);
}
}
However in the router, the next line i.e. console.log('Index router: ',User); is executed before User is obtained, printing Index router: Promise {<pending>}.
router.get('/', function(req, res, next) {
Posts.find({})
.populate('posted_by', 'username _id')
.then((allPosts) => {
return allPosts;
})
.then((list) => {
console.log("list: ",list);
if(list.length > 10)
list = list.splice(10, list.length-10);
res.statusCode = 200;
res.setHeader('Content-Type','text/html');
var User = authenticate.loggedIn(req); //Here I want the User before moving forward
console.log('Index router: ',User);
res.render('index', {
posts: list,
user: User
});
})
.catch((err) => next(err));
});
I have tried writing a different async function in which I as follows async function getuser(){ User = await authenticate.loggedIn(req); }, but the same problem occurs even then. Please help!
You have to use await:
[...]
var User = await authenticate.loggedIn(req);
[...]
Of course set the function as async:
router.get('/', async function(req, res, next) {
[...]
I'm sending multiple emails from my node.js server and I want to show progress on frontend about how many mails have been sent.
I wrote code that sending response only after every email has been sent however i need notify frontend about progress.
As far as I know i cannot send multiple responses on single http request so i kinda lost here.
My emailsend request handler:
const send = async (req, res) => {
//parsing request body
const {user, recievers, subject, text} = req.body;
//Getting sender credentials from db
const user = await UserSettings.findOne({ user }, 'MAIL');
//creating nodemailer transporter
const transporter = nodemailer.createTransport({
service: user.MAIL.SERVICE,
auth: {
user: user.MAIL.USER,
pass: user.MAIL.PASSWORD,
},
});
//sending email to every reciever
recievers.forEach(reciever=> {
const mailOptions = {
from: user.MAIL.USER,
to: reciever,
subject,
text
};
return transporter.sendMail(mailOptions, (error, info) => {
if (!error) {
console.log(`Email sent: ${info.response}`);
} else {
console.log(error);
}
});
});
return res.sendStatus(204);
};
In order to secure REST API I'm using middleware to check for user's JWT token and only allow that particular user to access his own data.
In auth.js
const jwt = require('jsonwebtoken')
const User = require('../models/user')
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '')
const decoded = jwt.verify(token, process.env.JWT_SECRET)
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })
if (!user) { // If no user is found
throw new Error()
}
// if there's a user
req.token = token
req.user = user
next()
} catch (e) {
res.status(401).send({ error: 'Please authenticate.' })
}
}
module.exports = auth
In one of the get/update router
router.get('/itemShipmentStatus', auth, async (req, res) => {
// Get the items shipment status from db.
})
However, I've noticed I need to create a new admin user (e.g. admin 1, admin2) to get and update the itemShipmentStatus for all the users. Is there a way to achieve user group authentication through the middleware (auth.js?)
Update:
The only solution I can think of is to add another "userGroup" field to the user document when creating a new user. Then in the middleware auth.js add in another condition to check if the user belongs to the admin group.
if (!user || user.userGroup !== 'Admin') { // If no user is found
throw new Error()
}
Is this the conventional way of doing it?
I would suggest adding permissions array stored in the user. That way you are more flexible. Then
const auth = (allowedPermission) => (async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '')
const decoded = jwt.verify(token, process.env.JWT_SECRET)
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })
if (!user) { // If no user is found
throw new Error()
}
if (!user.permissions.includes(allowedPermission)){
throw new Error() // Forbidden 403
}
// if there's a user
req.token = token
req.user = user
next()
} catch (e) {
res.status(401).send({ error: 'Please authenticate.' })
}
})
and in the route
router.get('/itemShipmentStatus', auth([admin, user]), async (req, res) => {
// Get the items shipment status from db.
})
Then it would be a matter to identify the correct code to run.
I would suggest considering the division of the api. A public api and an admin api. This is because conceptually a user may want to be admin and access its own itemShipmentStatus. So having
router.get('/itemShipmentStatus', auth([admin, user]), async (req, res) => {
// Get the items shipment status from db.
})
router.get('/admin/itemShipmentStatus', auth([admin]), async (req, res) => {
// Get the items shipment status from db of all user.
})
This allows an admin user to test the API as a normal user and get all the status as an admin.
A more conventional way of doing this would be to create an AuthRouter which extends the default express.Router and checks for allowed roles, so there will be no need to use middleware for each route.
Extending express.Router to check for roles:
const express = require('express');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
export default class AuthRouter extends express.Router {
addRoles(...roles) {
return this.use(checkAccessForRoles(...roles));
}
}
const checkAccessForRoles = (...roles) => async (req, res, next) => {
const token = req.header('Authorization').replace('Bearer ', '');
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token });
if (!roles.includes(user.role)) throw new Error('Forbidden');
req.user = user;
return next();
};
Using AuthRouter for ADMIN user role:
const adminRouter = new AuthRouter({
prefix: '/admin',
})
.addRoles(['ADMIN']);
adminRouter.get('/itemShipmentStatus', async (req, res) => {
// Get the items shipment status from db.
});