User authorization using custom middleware in loopback - javascript

I have made VerifyUserAccess middleware in loopback as below:
module.exports = function(options) {
return function storeCurrentUser(req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.UserModel.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
const LoopBackContext = require('loopback-context');
const loopbackContext = LoopBackContext.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
next();
});
};
};
And Also I added it in middleware.json as below:
...
"initial": {
"./middleware/verify_user_access": {},
...
}
...
But problem is that always loopbackContext I got null.
And the second question is that how to access currentUser result in API. I have done below:
Gameversionandtype.GetGameversionandtypeByGameTypeID = (ctx, callback) =>
{
const LoopBackContext = require('loopback-context');
const context = LoopBackContext.getCurrentContext();
const currentUserResult = context && context.get('currentUserResult');
console.log('currentUser.username: ', currentUserResult.id);
...
...
}
And Please let me know that how to set currentUser result and how to get it in API. I have referred documentation as well loopback-context npm module but I can not find any solutions. Thanking you in advance.

Related

Cannot set headers after they are sent to the client - Express JS

I am pretty much new to node / express and following a youtube tutorial to build a MERN Stack app. But my node server is giving this error
I tried restarting server many times it happening again and again. I got the idea it happens when we send two responses for one request but I don't think its the case here.
Btw here is the route it is pointing to in the error (in the try catch error response line)
// GET RANDOM
router.get("/random", verify, async (req, res) => {
const type = req.query.type;
let movie;
try {
if (type === "series") {
movie = await Movie.aggregate([
{ $match: { isSeries: true } },
{ $sample: { size: 1 } },
]);
} else {
movie = await Movie.aggregate([
{ $match: { isSeries: false } },
{ $sample: { size: 1 } },
]);
}
res.status(200).json(movie); //checked here by console logging it comes here only once
} catch (err) {
res.status(500).json(err); //the error pointing to this line
}
});
Just in case, here is the verify function code:
function verify(req,res,next) {
const authHeader = req.headers.token;
if(authHeader){
const token = authHeader.split(' ')[1];
jwt.verify(token,process.env.SECRET_KEY,(err,user) => {
if(err) res.status(403).json("Token is not valid");
req.user = user;
next();
})
} else{
return res.status(401).json("Unauthorized");
}
}

Unable to access user info object property - facebook chat api

I'm using Facebook chat api to create a simple cli script that will reply to messages that are sent to my facebook account. I'm trying to assign and get the user name and my name to use them inside the reply but they are always undefined. I think that the object property aren't assigned correctly. Is there a fix for this?
require('dotenv').config();
const fs = require('fs');
const fb = require('facebook-chat-api');
const path = require('path');
const appStateFile = path.format({ dir: __dirname, base: 'appstate.json' });
let currentUser = null;
if( !fs.existsSync(appStateFile) ){
//debug .env
console.log(process.env);
fb({email: process.env.FB_EMAIL, password: process.env.FB_PWD}, (err, api) => {
if(err){
return console.log(err);
}
console.log(api);
api.setOptions({
listenEvents: true
});
fs.writeFileSync(appStateFile, JSON.stringify(api.getAppState()));
let id = api.getCurrentUserID();
api.getUserInfo(id, (err, profile) => {
console.log(profile); // profile is logged correctly
currentUser = profile;
});
api.listenMqtt( (err, event) => {
if(err){
return console.log(err);
}
if(event.type === 'message'){
console.log(event.body)
api.getUserInfo(event.senderID, (err, user) => {
if(err){
return console.log(err);
}
console.log(user); // user object is logged correctly
api.sendMessage('...', event.threadID)
});
}
});
});
}else{
fb({appState: JSON.parse(fs.readFileSync(appStateFile))}, (err, api) => {
if(err){
return console.log(err);
}
console.log(api);
api.setOptions({
listenEvents: true
});
let id = api.getCurrentUserID();
api.getUserInfo(id, (err, profile) => {
console.log(profile);
currentUser = profile;
});
api.listenMqtt( (err, event) => {
if(err){
return console.log(err);
}
if(event.type === 'message'){
console.log(event.body)
api.getUserInfo(event.senderID, (err, user) => {
if(err){
return console.log(err);
}
console.log(user)
api.sendMessage(`FB Pager v1.0.\nHi ${user.name}!Your message was forwarded with an email to ${currentUser.name}.`, event.threadID)
});
}
});
});
}
I think the problem here is that api.getUserInfo is asynchronous.
So you would need to nest them to get it to work.
Or you can try this, since getUSerInfo allows you to add an array of user ids to get the data for:
api.listenMqtt((err, event) => {
if (err) {
return console.log(err);
}
if (event.type === "message") {
const currentUserId = api.getCurrentUserID();
const senderId = event.senderID;
api.getUserInfo([currentUserId, senderId], (err, ret) => {
if(err) return console.error(err);
// Ret should contain the two users
// See: https://github.com/Schmavery/facebook-chat-api/blob/master/DOCS.md#getUserInfo
console.log(ret);
});
}
});
Nesting user calls method:
api.listenMqtt((err, event) => {
if (err) {
return console.log(err);
}
if (event.type === "message") {
let currentUserId = api.getCurrentUserID();
api.getUserInfo(currentUserId, (err1, signedInUser) => {
if (err1) {
return console.log(err);
}
api.getUserInfo(event.senderID, (err2, userInMessage) => {
if (err2) {
return console.log(err);
}
console.log(signedInUser, userInMessage)
api.sendMessage("...", event.threadID);
});
});
}
});
After a lot of debug I've found the correct way to access the needed informations. Since the user informations after that are retrived are mapped to another object that is the userId, the only way to access to each property is to use a for loop. Initially I was thinking that this can be avoided but unfortunately it's necessary otherwise using only dot notation will result in undefined. This is how I've solved
api.getUserInfo(userId, (err, user) => {
let username;
if(err){
return console.log(err);
}
for(var prop in user){
username = user[prop].name;
}
api.sendMessage(`Hello ${username!}`, event.threadID);
});

SINON - an issue mocking a middleware

I have the following middleware in a Node.js REST API
const Authorized = (req, res, next) => {
if (!req.headers["authorization"]) {
res.status(401).send("Unauthorized");
} else {
jwt.verify(req.headers["authorization"], PRIVATE_KEY, (err, decoded) => {
if (err) {
res.status(403).send("Forbidden");
} else {
req.businessId = decoded.businessId;
req.roleId = decoded.roleId;
next();
}
});
}
};
As you can see I'm adding to variables to the request object
In the mockup of my tests I'm trying to do this:
sandbox = sinon.createSandbox();
sandbox.stub(Authorized, "Authorized")
.callsFake(async (req, res, next) => {
req.businessId = businessAux.id;
return next();
});
But this doesn't work and my actual function to be tested needs this variable:
listPermissionAndRolesByPk() {
this.app.get(`${config.API_PATH}/permissionrolebypk`, Authorized.Authorized, async (req, res) => {
const id = req.query.id;
const businessId = req.businessId;
if (!id) return res.status(400).send(messages[23].message.replace("${object}", "Permission Id"));
try {
const permission = await PermissionDAO.getPermissionAndRolesByPk(id, businessId ? businessId : 0);
if (permission) {
return res.status(200).json(permission);
} else {
return res.status(404).send(messages[8].message.replace("${object}", "Permission"));
}
} catch (error) {
return res.status(error.httpCode).send(error.message);
}
});
}
Any help will be appreciated.

How to return a value (access token) inside a function without promises?

const callback = (req, res) => {
// your application requests refresh and access tokens
// after checking the state parameter
if (state === null || state !== storedState) {
...
} else {
res.clearCookie(stateKey);
var authOptions = {...};
//rq = require-promise
rq.post(authOptions, function(error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token,
refresh_token = body.refresh_token;
// we can also pass the token to the browser to make requests from there
res.redirect('/#' +
querystring.stringify({
access_token: access_token,
refresh_token: refresh_token
}));
}
});
}
};
I have this function that I exported as a module so I can use in the main app.js as a middleware function when, after login, spotify redirects to the /callback.
Since I can't make it as a promise and after it retrieve the value with .then, I was wondering how could I get the access_token that will be generated after login in another module.
Thank you in advance.
You can share value between middleware and define it in req. For simpler explain, this is a example :
// the first middleware : checkAuth.js
const jwt = require('jsonwebtoken')
const authValidator = async (req,res,next) => {
// check token as requirement for auth user
let token = req.headers['authorization']
try {
let verifiedUser = await jwt.verify(token, 'yoursecret', (err, decode) => {
if (err) return res.send(err.message)
return decode
})
// assign verifiedUser to req
req.userData = verifiedUser
// passing to next middleware
next()
} catch (err) {
return res.send('no token provided')
}
}
exports.authValidator = authValidator
It's share to another middleware :
// second middleware : checkRole.js
const roleUser = async (req,res,next) => {
try {
// call the req.UserData and assign other value
if (req.UserData) req.userRole = 'Administrator'
// passing to next middleware
next()
} catch (err) {
return res.send('ooppsss')
}
}
exports.roleUser = roleUser
and in this route you can get them all :
const { Router } = require('express')
const router = Router()
const authValidator = require('/checkAuth.js')
const roleUser = require('./checkRole.js')
// arrange every middleware right here
router.get('/myRoute', authValidator, roleUser, async (req,res) => {
try {
// take all from before
return res.json({
...req.userData,
role: req.userRole
})
} catch (err) {
return res.send('oopsss')
}
})

node.js | ensureAdmin middleware function not working

I am working on a middleware function that should check in a db if the logged in user has the role = 2 before allowing access to the requested page. If the user does not have the role = 2 he should be redirected to the home page (/). I wrote following function to achieve that:
isAdmin = function(req, res, callback) {
let Users = require('./models/users');
Users.findOne({ 'steam.id': req.user.steam.id }, 'role', function(err, data) {
if(err) {
return callback(err, false);
} else {
if(data.steam.role === undefined || data.steam.role != 2) {
return callback(null, false);
} else {
if(data.steam.role === 2){
return callback(null, true);
}
}
}
});
};
The following function gets placed in the app.get(/admin) part of my routes file
function ensureAdmin(req, res, next) {
if (isAdmin()) {
return next();
}
console.log(colors.red('[ATTENTION] ') + colors.red('A non admin tried to access admin-only pages'));
res.redirect('/');
}
app.get:
app.get('/admin', ensureAuthenticated, ensureAdmin, function(req, res){
res.send('Admin panel!');
});
When I try to access the /admin page I just get a ReferenceError: isAdmin is not defined. Possibly there are more errors after this one that I can't solve so it would be great if anyone could tell me what I did wrong and fix the code if he wants. I am a node.js beginner :)
EDIT (new code):
module.exports = function(isAdmin){
var isAdmin = function(req, res, callback) {
if(req.user.steam.role === undefined || req.user.steam.role != 2){
return callback(null, false);
} else {
if(req.user.steam.role === 2){
return callback(null, true);
}
}
};
}
.
let isAdmin = require('../functions/isAdmin');
function ensureAdmin(req, res, next) {
if(isAdmin()) {
return next();
}
}
Do an export on your function isAdmin if you are in different files and do return that function as it's async
var isAdmin = function(req, res, callback) {
let Users = require('./models/users');
return Users.findOne({ 'steam.id': req.user.steam.id }, 'role', function(err, data) {
if(err) {
return callback(err, false);
} else {
if(data.steam.role === undefined || data.steam.role != 2) {
return callback(null, false);
} else {
if(data.steam.role === 2){
return callback(null, true);
}
}
}
});
};
export default isAdmin
Also, the call needs to be thenable
function ensureAdmin(req, res, next) {
isAdmin().then(response => {
next();
});
}
I noticed that you have written console.log res.redirect which will not make sense after calling next() in middleware. You can shift this console.log() prior to the next() call. Avoid res.redirect() in middleware
Last, Assuming that you are doing an import of a file as well as mentioned by #hansmaad
Frist you have to export your isAdmin function from the file where it is implemented
export default isAdmin
and then require it in the file where you want to use it
const isAdmin = require('../middlewares/isAdmin'); // wherever you've put this file
As your isAdmin function is async and returns a promise, you have to call next() when this promise resolved.
isAdmin().then(() => next(), () => res.redirect('/'));

Categories

Resources