How to pass parameter to function in middleware - javascript

So I wanted to pass a parameter to a middleware but I'm struggling on some points.
I have that function in the routes' handler:
router.get('/users', auth.required, userController.findAll);
Then it would go to the auth function which calls getTokenFromHeaders:
const auth = {
required: jwt({
secret: 'secret',
userProperty: 'payload',
getToken: getTokenFromHeaders,
}),
...
};
In getTokenFromHeaders function, the token is retrieved and checked, it looks like that:
const getTokenFromHeaders = (req) => {
...
return token; // Or null in case it's not there or incorrect
So my goal would be to pass a parameter like that auth.required('role') to check the user's role inside getTokenFromHeaders function (defining more auth functions would be fine as well (auth.admin, auth.whatever, ...)
I already tried modifying it as the following:
const auth = {
required: jwt({
secret: 'secret',
userProperty: 'payload',
getToken: getTokenFromHeaders(req, res, role),
}),
But it says that req and res is not defined.
Is there any way to do that?

get method receives a path and 2 callbacks where first one is a middleware:
router.get('/users', auth, userController.findAll);
Middleware is like findAll function and is executed before:
var auth = function (req, res) {
// Get token from header (http://expressjs.com/en/api.html#req.get)
var token = req.get("myToken");
// TODO Validate token:
var isValid = someFunction(token);
if (!isValid) {
// ************
// TODO Check if user can access this resource
// ************
res.json("not authorized");
}
// Go to findAll (next callback)
};
I suggest you take at look in docs specially in app.use to understand better how middleware works in express.

I think that :
1) Call of request
app.use('/users', required);
2) Check authentification
const required = (req, res, next) => {
const auth = {
required: jwt({
secret: 'secret',
userProperty: 'payload',
getToken: getTokenFromHeaders(req),
}),
...
};
if (auth.required) next();
else // error
}
3) Next step
router.get('/users', userController.findAll);
4) userController.findAll
userController.findAll will send the response with with the parameters recovered (req and res)

Related

Passport JS OAuth2Strategy does't fire callback

After updating my dependencies at service I'm creating I faced the issue that my Passport JS strategy, used along with OAuth2Strategy stoped working properly.
Here are the prerequisite:
Node: 14.15.1
pnpm: 6.29.0
passport: 0.5.2
passport-oauth2 1.6.1
express-session 1.17.2
Strategy is initialised like follows:
const passport = require('passport');
const config = require('../../../../config');
const OAuth2Strategy = require('passport-oauth2');
const axios = require('axios');
const MyStrategy = new OAuth2Strategy({
// state: true,
authorizationURL: config.myapi.authorizationURL,
tokenURL: config.myapi.tokenURL,
clientID: config.myapi.clientId,
clientSecret: config.myapi.clientSecret,
callbackURL: config.myapi.callbackURL,
passReqToCallback: true,
}, () => {console.log('Fire!')}); // <- This line should be called, but it is not!
passport.use('oauth2', MyStrategy);
*Obviously, the part where fire is written should be replaced by callback function, but I replaced it for more cleaner code
And routes go this way
...
routes.get('/oauth/myapi', async (req, res, next) => {
const authParams = {
session: true,
scope: 'read, create',
state: req.csrfToken(),
};
return passport.authenticate('oauth', authParams,
async (err, passportUser, info) => {
if (err) {
return next(err);
}
if (passportUser) {
const user = passportUser;
user.token = passportUser.generateJWT();
return res.json(user.toAuthJSON());
}
res.status(400).json({error: info});
})(req, res, next);
});
routes.get('/oauth/myapi/callback',
async (req, res, next) => {
return passport.authenticate('oauth', {
// failWithError: true,
successRedirect: '/dashboard',
failureRedirect: '/login/oauth/myapi/failed'
})(req, res, next);
});
...
So in a callback I do receive response from third service and it looks like this
{
"code":"2QoCKOzHQbCdJID4m...pwHv4M1RqUKjKF",
"state":"dS9Gagcc-....a_yJ71YU",
"user_id":"101"
}
But when callback route attempts to execute passport.authenticate I receive
$ Error: Failed to obtain access token
$ at /Users/number16/Documents/GitHub/Video-Mixer-Node/node_modules/.pnpm/passport-oauth2#1.6.1/node_modules/passport-oauth2/lib/strategy.js:178:49
Debugging didn't help much either.
It seems to me that I do something wrong or I should update my code as some breaking change might require.
The problem seems to be caused by switching from 0.4.1 to 0.5.2 passport
Please, provide me with suggestions on what might cause this issue and how to resolve it.
Thanks in advance

(Express)How to pass a value from function to another one in express.Router().get

How I can pass value from function to another one at router.get
router.get('/someurl', (req, res, next) => {
const token = req.headers.authorization.split(' ')[1] //jwtToken
const jwt = jwt.verify(
token,
jwtSecret
)
...do something to pass value to the next function
}, )
You can use res.locals to do that
An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during that request / response cycle (if any).
So in your case
router.get(
"/someurl",
(req, res, next) => {
const token = req.headers.authorization.split(" ")[1]; //jwtToken
const jwt = jwt.verify(token, jwtSecret);
// pass to res.locals so I can get it in next() middleware
res.locals.token = token;
next();
},
(req, res, next) => {
// inside the next() middleware
// get token from res.locals
const previousToken = res.locals.token;
}
);

How done function works in passport js?

I have auth function, I want it to authenticate my user route.
// auth.js
function auth(request, response, next) {
passport.authenticate('jwt', { session: false }, async (error, token) => {
if (error || !token) {
response.status(401).json({ message: 'Unauthorized' });
}
next(token);
})(request, response, next);
next()
}
module.exports = auth;
And heres my jwt strategy
// passport.js
passport.use(
new JwtStrategy(opts, (payload, done) => {
console.log('payload', payload) // this works
User.findById(payload.id)
.then(user => {
if (user) {
console.log('here user', user) // this also works
return done(null, user);
}
return done(null, false);
})
})
);
But why when I console log my request It doesn't show me the user that I already declare in done(null, user)
const auth = require('../auth.js')
router.get('/', auth, async (req, res) => {
console.log(req.user) // return undefined
// other code
});
There are a couple issues that I can see:
From your auth() middleware function, your are calling next() before passport has had a chance to authenticate the incoming request - which happens asynchronously. You should remove the synchronous call to next() there, and defer to passport.authenticate() callback to handle this.
In passport.authenticate() callback, you're calling next() with an argument - express will take this as an error occurring and jump to the next error middleware in line.
Edit: I also checked the signature of the passport.authenticate() callback and it seems to be (error, user, info) - not (error, token).
Edit 2: It also seems like when passing passport.authenticate() a custom callback, it becomes your responsability to expose user on the req object by calling passport req.login() function. Please take a look here:
http://www.passportjs.org/docs/authenticate/ (custom callback section at the end)
http://www.passportjs.org/docs/login/

Role based authorisation for Node js or Express js

Is there any library for role-based authorization in node.js or Express js? Like: Super Admin, Admin, Editor, User etc.
You can use role based middleware. thanks to joshnuss.
I am using it for my api having different users like developers, customers, employee, admin.
works like a charm
index.js
import express from "express";
import loadDb from "./loadDb"; // dummy middleware to load db (sets request.db)
import authenticate from "./authentication"; // middleware for doing authentication
import permit from "./permission"; // middleware for checking if user's role is permitted to make request
const app = express(),
api = express.Router();
// first middleware will setup db connection
app.use(loadDb);
// authenticate each request
// will set `request.user`
app.use(authenticate);
// setup permission middleware,
// check `request.user.role` and decide if ok to continue
app.use("/api/private", permit("admin"));
app.use(["/api/foo", "/api/bar"], permit("owner", "employee"));
// setup requests handlers
api.get("/private/whatever", (req, res) => response.json({whatever: true}));
api.get("/foo", (req, res) => response.json({currentUser: req.user}));
api.get("/bar", (req, res) => response.json({currentUser: req.user}));
// setup permissions based on HTTP Method
// account creation is public
api.post("/account", (req, res) => req.json({message: "created"}));
// account update & delete (PATCH & DELETE) are only available to account owner
api.patch("/account", permit('owner'), (req, res) => req.json({message: "updated"}));
api.delete("/account", permit('owner'), (req, res) => req.json({message: "deleted"}));
// viewing account "GET" available to account owner and account member
api.get("/account", permit('owner', 'employee'), (req, res) => req.json({currentUser: request.user}));
// mount api router
app.use("/api", api);
// start 'er up
app.listen(process.env.PORT || 3000);
// middleware for doing role-based permissions
export default function permit(...allowed) {
const isAllowed = role => allowed.indexOf(role) > -1;
// return a middleware
return (req, res, next) => {
if (req.user && isAllowed(req.user.role))
next(); // role is allowed, so continue on the next middleware
else {
response.status(403).json({message: "Forbidden"}); // user is forbidden
}
}
}
dummy middleware for db (set's request.db)
export default function loadDb(req, res, next) {
// dummy db
request.db = {
users: {
findByApiKey: async token => {
switch {
case (token == '1234') {
return {role: 'superAdmin', id: 1234};
case (token == '5678') {
return {role: 'admin', id: 5678};
case (token == '1256') {
return {role: 'editor', id: 1256};
case (token == '5621') {
return {role: 'user', id: 5621};
default:
return null; // no user
}
}
}
};
next();
}
middleware for authentication
export default async function authorize(req, res, next) {
const apiToken = req.headers['x-api-token'];
// set user on-success
request.user = await req.db.users.findByApiKey(apiToken);
// always continue to next middleware
next();
}

req.user is undefined when using PassportJS, SequelizeJS and JWT tokens

I already checked multiple answers here on Stackoverflow, and also went through on the documentation but I still cannot find out what could be the problem. In my application I'm using SequelizeJS to access to my mySQL database and now I'm trying to secure my REST API endpoints with PassportJS using the JWT Strategy.
./app.js
// ...
// passport
app.use(passport.initialize());
require('./config/passport')(passport);
// ...
./config/passport.js
var passport = require('passport');
var passportJwt = require('passport-jwt');
var models = require('../models');
var config = require('./config');
var ExtractJwt = passportJwt.ExtractJwt;
var Strategy = passportJwt.Strategy;
module.exports = function(passport) {
var params = {
secretOrKey: config.jwt.secret,
jwtFromRequest: ExtractJwt.fromAuthHeader()
};
passport.use(new Strategy(params, function(jwt_payload, done) {
models.User.findOne({
where: {
id: jwt_payload.id
}
}).then(
function(user) {
if (user) {
done(null, user);
} else {
done(null, false);
}
},
function(err) {
return done(err, false);
}
);
}));
};
I'm trying to get the user entity from the request of this simple route:
var router = express.Router();
// ...
router.route('/user/me', passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});
I already created another route which returns a JWT token based on the provided username and password. When I call the /user/me endpoint I attach the JWT token into the header, for example:
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.M9z3iWNdjAu4THyCYp3Oi3GOWfRJNCYNUcXOw1Gd1Mo
So, my problem is that when I call the /user/me endpoint with a token, the req.user will be undefined and I cannot figure it out what is the reason.
Thank you in advance for your help!
Your route definition seems to be wrong: router.route doesn't accept a middleware in its second argument, so authentication does not happen at all.
It should be smth like
var router = express.Router();
// ...
router.route('/user/me')
.all(passport.authenticate('jwt', { session: false }))
.get(function(req, res) {
console.log(req.user);
res.json(req.user);
});

Categories