Asynchronous function call is not working, synchronous is working - javascript

I have a router with the following route:
router.post('/login', async (req, res, next) => {
try {
console.log(req.body);
const { username, password } = req.body;
const identity = await userService.getUserInfo(username, password);
if (!identity.authenticated) {
return res.json({});
}
const requiredTenantId = process.env.TENANT_ID;
const tenant = identity.tenants.find((it) => it.id === requiredTenantId);
if (requiredTenantId && !tenant) {
return res.json({});
}
const userResponse = {
...identity,
token: jwt.sign(
identity,
envVars.getVar(envVars.variables.AUTH_TOKEN_SECRET),
{ expiresIn: '2h' }
)
};
return res.json(userResponse);
} catch (err) {
return next(err);
}
});
Which is basically an asynchronous function.
This is the working test sample:
const request = require('supertest');
const user = require('../../routes/user');
describe('Test user login path', () => {
test('If authorized, it should response with authorized JWT token', () => {
request(user).
post('api/user/login/').
send({
username: 'admin',
password: 'admin'
}).
expect(200);
});
});
If I add async before the function call and await before request user:
test('If authorized, it should response with authorized JWT token', async () => {
await request(user).
the test will fail with the following error:
connect ECONNREFUSED 127.0.0.1:80
Can somebody explain why it is like that? Because in the router I'm using the asynchronous route function.

That's because the supertest expects to be given an express application, not a router
You can create a test-purpose app, mount the user route:
const app = express();
app.use(bodyParser.json());
app.use("/api/user", user);
app.listen(3000);
and pass it to the request
await request(app)
.post("/api/user/login")
working example

Related

How to properly give next() in the middleware in node app

I am trying to verify the bearer token which has been generated after the login request. I have segregated the codes for controllers, middlewares and routes.
The login is working fine and is able to generate the token but when I try to verify it I get the following error:
Error: Route.post() requires a callback function but got a [object Undefined]
at Route.<computed> [as post] (C:\Users\Desktop\loginapi\node_modules\express\lib\router\route.js:211:15)
at Function.proto.<computed> [as post] (C:\UsersDesktop\loginapi\ at Module.require (node:internal/modules/cjs/loader:1028:19)
at require (node:internal/modules/cjs/helpers:102:18) at Object.<anonymous> (C:\Users\Desktop\loginapi\index.js:15:20)
[nodemon] app crashed - waiting for file changes before starting...
Below is the complete Code:
Server.js //Main file
const express = require('express')
const app = express()
require('dotenv').config();
const port = process.env.PORT;
console.log(port)
//initialize middleware
app.use(express.json())
//import routes
const authRoutes = require('./src/routes')
// initialize routes
app.use('/abc', authRoutes)
// console.log();
//app start
const appStart = () => {
try {
app.listen(port, ()=> {
console.log(`The app is listening at http://localhost:${port}`)
})
} catch (error) {
console.log(`${error.message}`)
}
}
appStart()
routes/index/js
const { Router} = require('express')
const userController = require('../controllers/index')
const { verifyToken } = require('../middlewares/verifyTokens')
const router = Router()
router.post('/login', userController.login)
router.post('/profile',verifyToken ,userController.profile)
module.exports = router
middlewares/verifyTokens.js
// Function to verify whether the bearer token is correct or not
function verifyToken(req, resp, next) {
const bearerHeader = req.headers['authorization'];
if (typeof bearerHeader !== 'undefined') {
const bearer = bearerHeader.split(" ");
const token = bearer[1];
req.token = token;
next();
} else {
resp.send({
result: 'Invalid Token'
})
}
}
module.exports = verifyToken
contollers/index.js
const { sign } = require('jsonwebtoken')
const secret = process.env.SECRET;
//login function
const login = async (req, res) => {
payload = {
"email": "abc#gmail.com",
"password": "user123"
}
console.log(payload)
try {
sign(payload, secret, (err, token) => {
try {
res.json({
token
})
// console.log(typeof resp.json)
} catch (err) {
res.send(err.message);
}
})
} catch (error) {
console.log(error.message)
return res.status(500).json({
error: error.message
})
}
}
const profile = async (req,res) => {
try {
return res.status(200).json({
success: true,
message: 'In Profile',
})
} catch (error) {
console.log(error.message)
return res.status(500).json({
error: error.message
})
}
}
const userControllers = {
login,
profile
}
module.exports = userControllers
There is some problem with the middleware verifyTokens ,I have gone through other answers but I am not getting it. Can someone please point it out as to where I am going wrong. It will be very helpful. Thank you
Import verifyTokens without { } it will solve the problem.
In routes/index/js :
const verifyTokens = require('../middlewares/verifyTokens')
Instead of this
router.post('/login', userController.login)
router.post('/profile',verifyToken ,userController.profile)
try this one
router.post('/login',(req, res) => {
userController.login
});
router.post('/profile',
(req, res, next) => {
verifyToken
},
(req,res,next) => {
userController.profile
}
);

CRUD Node JS (Cannot Post)

I'm currently trying my hand at "basic authentication" for users. I can't now get a http response back to my query. The path of the request is 'AuthenticationService' --> 'UserService'. But from 'UserService' I can't get to 'UserRoute' and therefore not to 'httpServer'. The correct order should be: AuthenticationService -->UserService --->Userroute-->httpServer If I change in AuthenticationRoute.js router.post('/', authenticate); to router.post('/authenticate', authenticate) I get a http response, but I dont transmit any data....What do I have to do ?
UserService.js
async function authenticate({ username, password }) {
const user= User.find({userName:username} && {password:password})
/* const user = User.find(u => u.userName === so && u.password === password); */
if (user) {
const { password, ...userWithoutPassword } = user;
return userWithoutPassword;
}
}
module.exports = {
authenticate
}
UserRoute.js
var userService = require('./UserService')
router.post('/', authenticate);
function authenticate(req, res, next) {
userService.authenticate(req.body)
.then(user => user ? res.json(user) : res.status(400).json({ message: 'Username or password is incorrect' }))
.catch(err => next(err));
}
module.exports = router;
AuthenticationService.js
async function basicAuth(req, res, next) {
// make authenticate path public
if (req.path === '/') {
return next();
}
if (!req.headers.authorization || req.headers.authorization.indexOf('Basic ') === -1) {
return res.status(401).json({ message: 'Missing Authorization Header' });
}
// verify auth credentials
const base64Credentials = req.headers.authorization.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [username, password] = credentials.split(':');
console.log(username+password);
const user = await userService.authenticate({ username, password });
if (!user) {
return res.status(401).json({ message: 'Invalid Authentication Credentials' });
}
req.user=user
next();
}
module.exports = {
basicAuth
}
AuthenticationRoute.js
var express = require('express');
var router = express.Router();
var authenticationService=require('./AuthenticationService')
router.post('/authenticate', authenticationService.basicAuth);
module.exports=router;
httpServer.js
const userRoutes = require('./endpoints/user/UserRoute')
const authenticationRoutes= require('./endpoints/authentication/AuthenticationRoute')
var axios = require('axios');
app.use(authenticationRoutes);
app.use(userRoutes);
The request I try to send is:
POST http://localhost:8080/authenticate
Authorization: Basic YWRtaW46MTIz

Jsonwebtoken not working as expected JWT Expired

Hello can some help with this, I can't seems to figure out why I always got en error TokenExpiredError: jwt expired I just created the token.
Here's what I wanted to do.
1.) when user logged in, response contains the accessToken and refreshToken
2.) call the auth/token every 30 seconds to generate new accessToken.
auth/token api will accept refresh_token as request data and the accessToken as Bearer Authorization, however in I always got jwt expired. It fails on the authentication middleware.
However when I used the accessToken I got from logged in api, I got jwt expired error.
JWT Service
sign(payload, expiresIn = '15m') {
return jwt.sign(payload, this.privateKey, {
algorithm: 'RS256',
expiresIn,
});
}
verify(token) {
return jwt.verify(token, this.publicKey, { algorithms: ['RS256'] });
}
Controller.js
// login api
login(req, res, next) {
const { body: { email, password } } = req;
this.accountService.findByEmail(email)
.then(async (data) => {
const accessToken = this.jwtService.sign({ data}, '2m');
const refreshToken = this.jwtService.sign({ data}, '1h');
return res.send(new SuccessResponse(200, {accessToken, refreshToken}));
})
.catch((error) => {
next(error);
});
}
}
// auth/token api
authToken(req, res, next) {
const { body: { refresh_token } }= req;
const payload = { refresh_token };
const newAccessToken = this.jwtService.sign({ payload }, '1m', 'RS256');
}
authenticate.middleware.js
export const authenticate = (req, res, next) => {
const authorizationBearer = req.get('authorization');
const accessToken = authorizationBearer.replace('Bearer ', '');
const decodedData = jwtService.verify(accessToken);
if (decodedData) {
next();
}
}
Did I miss something or did I do anything wrong?
Library Used:
jsonwebtoken: 8.5.1
express: 4.17.1

React doesn't get express session login details sending me 401 error

I'm trying to make a page that only registered users can access to that page and I'm checking it by middleware at the server side, but when im trying to access the page I get 401 error because the session.user is undefined, while when im trying my code through "postman" it works great without any issues, here is the middleware:
function isLoggedIn(request, response, next) {
if (!request.session.user) {
response.status(401).send("You are not logged in!");
return;
}
next();
}
module.exports = isLoggedIn;
And here is the controller:
router.get("/", isLoggedIn,async (request, response) => {
try {
const vacations = await vacationsLogic.getAllVacationsAsync();
response.json(vacations);
}
catch (err) {
response.status(500).send(err.message);
}
});
and here is the login router:
router.post("/login", async (request, response) => {
try {
const credentials = request.body;
const user = await authLogic.login(credentials);
if (!user) {
response.status(401).send("Illegal username or password");
return
}
request.session.user = user;
response.json(user);
}
catch (err) {
response.status(500).send(err.message);
}
})
and here is the register router:
router.post("/register", async (request, response) => {
try {
const user = new User(0, request.body.firstName, request.body.lastName, request.body.username, request.body.password, 0);
// if username already exits -
// return some error(400) to the client.
const addedUser = await authLogic.register(user);
// Save that user in the session:
request.session.user = addedUser;
response.status(201).json(addedUser);
}
catch (err) {
response.status(500).send(err.message);
}
})

How to create user group security for REST API using middleware

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.
});

Categories