can /path/{?} be used in path in nodejs? - javascript

I am relatively new to node.js and trying to write a route which will check the authorization of all the users if the mentioned endpoint starts with /api.
I read that for optional value ? can be used {_id?} like this but is it possible to use it like {?}.
and How do I call this route into another route which actually does the job of GET method?
Currently the method looks like this
server.route({
method: 'GET',
path: '/api/something/nothing/{_id?}',
handler: function (request, reply) {
Controller.vcontroller.get(request.headers.authorization, request.params, function (err, success) {
console.log(request.headers);
console.log(request.headers.authorization);
if (err) {
reply(func.sendError(err));
} else {
reply(func.sendSuccess(APP_CONSTANTS.STATUS_MSG.SUCCESS.DEFAULT, success)).code(200);
}
});
},
config: {
description: 'desc',
tags: ['api', 'order'],
validate: {
headers: func.authorizationHeaderObj,
params: {
order_id: Joi.string().required().trim(),
_id: Joi.string().optional().trim()
},
failAction: func.failActionFunction
},
plugins: {
'hapi-swagger': {
responseMessages: APP_CONSTANTS.swaggerDefaultResponseMessages
}
}
}
}
});
when I pass endpoint /api it should check the authorization for all the users for GET PUT POST & DELETE respectively.
In other words I want global authorisation which is route specific
Can you suggest a way to implement it?

To check if all requests to endpoints starting with /api are authorised you can use middleware, i.e.
app.use('/api', (req, res, next) => {
// this uses passport.js for auth, you could use something else
if(req.isAuthenticated()) {
return next();
}else{
return res.status(401).send();
}
});

I'm not entirely clear that I'm understanding your question, so I'll restate what I think you're asking and then answer. If I am not understanding, please clarify.
What I think you're asking is whether you can run a check anytime someone hits a URL path that starts with /api, probably for user authorization purposes.
The answer is, yes, you can just incorporate that into a middleware (though I strongly recommend Passport for all your authentication needs.
That said, if you're using Express, then you can just add middleware at the start of your routing like so:
app.use('/api', (req, res, next) => {
// trivial implementation, not for production
const users = ['bob', 'mari', 'hasan'];
if (users.indexOf(req.user) >= 0) {
// user is authorized
return next();
}
// Forbidden
return res.sendStatus(403);
});
Put that before your other routes, and any route starting with /api will pass through this first. Obviously, I'm assuming you have populated req.user here somehow, but if you're using Passport as I suggested that will be done for you.

Related

Passport.js authentication with node.js

I am from laravel background trying to implement passport.js authentication in my sails.js app
the documentation is minimal and also hard to understand
Here is my login.js
module.exports = {
/*
*Normal login
*/
login:function (req,res) {
//user input
console.log('email'+req.parm('email')+'password'+req.param('password'))
passport.authenticate(
'local-login',
function (req,res) {
}
}
passport.use('local-login',new LocalStrategy(function (username,password,done) {
if(username=='test')
return done(null,username);
else
return done(null,false,{message:'Invalid userinfo'})
}));
but the passport.authenticate never fired
From their documentation
app.post('/login',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
also what is the meaning of this If this function gets called, authentication was successful.
Found this tutorial http://iliketomatoes.com/implement-passport-js-authentication-with-sails-js-0-10-2/ but its explanation so poor
The tutorial is pretty old as it's for sails 0.10, but it is still valid. You are using the passport-local strategy. When you define your strategy you've got an extra parameter. Remove this 'local-login' parameter.
You currently have:
passport.use('local-login',new LocalStrategy(function...
Replace the above with:
passport.use(new LocalStrategy(function...
Then when you call authenticate specify 'local' not 'local-login' as the strategy, so you have:
passport.authenticate(
'local',
function (req,res)...
'local' goes with passport-local. If you we're using the passport-http-bearer strategy then you would call
passport.authenticate('bearer', function...
I usually put my strategy definitions in /config/bootstrap.js along with the session serialization and helpers for finding the user. Then my controller-->service makes the passport.authenticate call.

Express.js middleware extra (fourth) argument

On the optional arguments to an Express.js middleware , probably a fourth one apart from being an error handling middleware, I have a use case where it comes useful. It may be achieved by a different way, but anyways.
I want to make some api routes permission checked, the middleware I wish to write should look up database for a requesting user for how many reputation points (integer) he/she has. I defined an object to hold privileges and reputation points required as key value pairs. Later, middleware should look up this privileges object to see if the user has greater than or equal reputation points for a corresponding action. I want to pass this corresponding action name which is a key in the privileges object as a different string for each route. This can be achieved by sending the string actionNames via requests to the routes, but I find it less secure (data in request can be tampered to have an action name that a malicious user has access to and to have required data fields for another wished but not permitted action).
All the case is just like that of SE's.
By the way, apparently I also need route based (not Router based) middleware mounting, I am not sure if Express.js supports, but this is another story.
Maybe one can describe and ask this use case as can I parametrize a middleware function with my parameters, not just by incoming req and res objects?
How can I achieve this use case with Express.js middleware? Or should I use another mechanism?
/// Privilege Check middleware
// used per route with corresponding actionName
// signature function (request, response, next, actionNameOneOfKeysInPrevilegesObject::String)
var privilegeCheck = function (req, res, next, actionName) {
db.one(
`
SELECT reputation FROM users WHERE id = $(id)
`,
{id: req.user.id} // remember req.user was set by jwt.sign(data) during login or signup with demanded data; here it is {id:, name:, username:,}
)
.then(function (data) {
if(data >= privileges[actionName]) {
next();
}
else {
res.status(403).json({errorMessage: "You need to have " + privileges.questionUpvote + " reputation to upvote."});
}
})
.catch(function (error) {
console.error(error);
})
};
// reputations needed for privileged actions
var privileges =
{
questionAsk: 5,
answer: 15,
acceptAnswer: 0,
comment: 5,
questionEdit: 20,
answerEdit: 20,
commentsEdit: 0,
postsUpvote: 30,
postsDownvote: 30,
commentsUpvote: 5,
questionUpvote: 10,
questionDownvote: 125,
}
Use route-specific middleware like so:
function privilegeCheck(actionName) {
return function (req, res, next) {
db.one(
`
SELECT reputation FROM users WHERE id = $(id)
`,
{id: req.user.id} // remember req.user was set by jwt.sign(data) during login or signup with demanded data; here it is {id:, name:, username:,}
)
.then(function (data) {
if(data >= privileges[actionName]) {
next();
}
else {
res.status(403).json({errorMessage: "You need to have " + privileges.questionUpvote + " reputation to upvote."});
}
})
.catch(function (error) {
console.error(error);
})
;
}
};
// Then, for each of your routes, invoke your privilegeCheck() function
// as an "in-between" argument between your path and your route handler
// function.
app.get('/my/route', privilegeCheck("myActionName"), (req, res) => {
res.status(200).send('handled /my/route');
});
See the "Application-level middleware" section at http://expressjs.com/en/guide/using-middleware.html, starting from "This example shows a middleware sub-stack that handles GET requests to the /user/:id path."
But the documentation there does not show that you can chain functions in the app.get(). You can see that at
https://scotch.io/tutorials/route-middleware-to-check-if-a-user-is-authenticated-in-node-js
In fact, you can have as many "middleware" (ie three-argument function) arguments to any of Express's route handling functions (get(), put(), post(), delete()) as you need.
Unfortunately, Express is coded so that if your middleware function has exactly four parameters, it is considered error handling middleware. IMO this was not a good design decision.
To do what you want to do, the canonical way to do is to attach data to the request stream (req).
For example:
app.use(function(req,res,next){
req.data = {foo:'bar'};
});
app.use(function(req,res,next){
const data = req.data;
}):
Coming from languages like Java, I thought this was some of the craziest coding ever, but it works because req is unique to that request + JS is "single-threaded".

Authenticating node API with passport-jwt

I'm trying to setup JWT authentication using passport-jwt. I think I've taken the right steps, but a test GET won't succeed and I don't know how to debug it.
Here's what I've done:
setup passport-jwt straight out of the doc as much as possible
var jwtOptions = {
secretOrKey: 'secret',
issuer: "accounts.examplesoft.com", // wasn't sure what this was, so i left as defaulted in the doc
audience: "yoursite.net" // wasn't sure what this was, so i left as defaulted in the doc
};
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeader();
passport.use(new JwtStrategy(jwtOptions, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
// or you could create a new account
}
});
}));
Added a token result to my user /login endpoint
var jwt = require('jsonwebtoken');
// ...
exports.postLogin = function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) throw err;
if (!user) {
return res.send({ msg: 'Login incorrect' });
}
req.logIn(user, function(err) {
if (err) throw err;
var secretOrKey = jwtOptions.secretOrKey;
var token = jwt.sign(user, secretOrKey, {
expiresIn: 631139040 // 20 years in seconds
});
res.send({ user: user, jwtToken: "JWT " + token });
});
})(req, res, next);
};
Things were looking good up to here. I can login a user (using passport local auth) and the response was a I hoped...
{
"user": {
"_id": "56c8b5bd80d16ef41ec705dd",
"email": "peachy#keen.com",
"password": "$2a$10$zd ... etc.",
"__v": 0,
},
"jwtToken": "JWT eyJ0eXAiOiJ .... etc." }
I created an unprotected test route like this...
// in my routes file
app.get('/user/tokenTest', user.tokenTest);
And in my controller, a simple endpoint...
exports.tokenTest = function(req, res) {
console.log(req.headers);
res.send("token test!!");
};
And GET-ing that works fine, too.
But then I try to protect that route like this:
app.get('/user/tokenTest', passport.authenticate('jwt', { session: false }),
user.tokenTest);
After I do that, nothing but sadness. I send a request like this:
curl -k 'https://localhost:3443/user/tokenTest' -H 'Authorization: JWT eyJ0eXAiOiJ... etc.'
And always, always get a 401:
Unauthorized
Console logs in the controller don't seem to execute, neither does logging in the passport.use strategy method. I've tweaked and tweaked, but I'm a little lost. The passport-jwt doc just supplies the example, and virtually no other help.
Please, any ideas about either a mistake that I'm making above, or at least how to go about debugging??
For any poor soul that follows me here: the passport-jwt doc implies that the auth header should look like this...
Authorization: JWT JSON_WEB_TOKEN_STRING.....
That turned out to be misleading (for me, anyway).
Fortunately, thanks to this article I was able to learn how the token is built. (The token's prefix up to the first '.' is the base64 encoding of the scheme. That "JWT " at the front was noise that prevented the validation from working.
So the fix was to change the token returned by the user controller from:
res.send({ user: user, jwtToken: "JWT " + token });
To the simpler:
res.send({ user: user, jwtToken: token });
Phew. Is it me, or is it really a bummer how inadequately these things are explained in so many node package docs??
I may be late but I had a similar problem, and I have another solution. You can use this options.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT') to extract the JWT token from authentication header with the following format:
Authorization: JWT JSON_WEB_TOKEN_STRING.....
Here is the documentation I used: https://github.com/themikenicholson/passport-jwt
Extracting the JWT from the request
There are a number of ways the JWT may be included in a request. In
order to remain as flexible as possible the JWT is parsed from the
request by a user-supplied callback passed in as the jwtFromRequest
parameter. This callback, from now on referred to as an extractor,
accepts a request object as an argument and returns the encoded JWT
string or null. Included extractors
A number of extractor factory functions are provided in
passport-jwt.ExtractJwt. These factory functions return a new
extractor configured with the given parameters.
fromHeader(header_name) creates a new extractor that looks for the JWT in the given http header
fromBodyField(field_name) creates a new extractor that looks for the JWT in the given body field. You must have a body parser configured in order to use this method.
fromUrlQueryParameter(param_name) creates a new extractor that looks for the JWT in the given URL query parameter.
fromAuthHeaderWithScheme(auth_scheme) creates a new extractor that looks for the JWT in the authorization header, expecting the scheme to match auth_scheme.
fromAuthHeaderAsBearerToken() creates a new extractor that looks for the JWT in the authorization header with the scheme 'bearer'
fromExtractors([array of extractor functions]) creates a new extractor using an array of extractors provided. Each extractor is attempted in order until one returns a token.

Express user authentication middleware, how much should it do?

I'm trying to learn Express session and authentication handling.
For example:
app.post('/login', authCredentials, function(req, res) {
console.log("second")
});
function authCredentials(req, res, next) {
//this happens first
console.log(req.body) // => { username: etc, password: etc }
next();
}
My question is just how much should my authCredentials function do?
For example if the credentials are correct, I can do something like
res.redirect('/index'). Once I do that, however, what purpose does the second function have?
Other questions:
How would I handle invalid credentials?
If I make authCredentials just return true or false depending on the credentials, doesn't that break the middleware flow because it would never invoke next()?
Is it possible to access anything in authCredentials in the anonymous callback after it? Basically in the function(req, res) { }?
The answer depends on your authentication strategy i.e. are you using session identifiers, access tokens, etc.
In either case I suggest that you break out the credential exchange (aka login) from the authentication.
function usernamePasswordExchange(req,res,next){
var username = req.body.username;
var password = req.body.password;
callToAuthService(username,password,function(err,user){
if(err){
next(err); // bad password, user doesn’t exist, etc
}else{
/*
this part depends on your application. do you use
sessions or access tokens? you need to send the user
something that they can use for authentication on
subsequent requests
*/
res.end(/* send something */);
}
});
}
function authenticate(req,res,next){
/*
read the cookie, access token, etc.
verify that it is legit and then find
the user that it’s associated with
*/
validateRequestAndGetUser(req,function(err,user){
if(err){
next(err); // session expired, tampered, revoked
}else{
req.user = user;
next();
}
});
}
app.post('/login',usernamePasswordExchange);
app.get('/protected-resource',authenticate,function(req,res,next){
/*
If we are here we know the user is authenticated and we
can know who the user is by referencing req.user
*/
});
Disclaimer: I work at Stormpath and we spend a lot of time writing
authentication code :) I just wrote our newest library, stormpath-sdk-express,
which has a concrete implementation of my suggestions
You want to add your authCredentials middleware to every end point that needs authentication. app.post('/login') usually does not need any as you want to access this end point to actually get credentials in the first place.
When credentials are correct/valid you simply invoke next() and the workflow will jump to the next middleware or the actual end point. If there was an error, invoke next() with an error object like next(new Error('could not authenticate'); for instance. Add an error route to your general routing and the error will be handled there:
app.use(function(err, req, res, next) {
res.render('error', err);
});
Should be answered by now.
A middleware does not return a value. It either calls next() or ends the process differently by calling res.send().
There are different approaches to pass variables from one middleware to another. The most common is probably to attach the desired value to the req parameter.
authenticate is an asychronous function in the following example:
function authCredentials(req, res, next) {
authenticate(req.body, function(err, user) {
if (err) {
return next(err);
}
req.user = user;
next();
});
}

Easy way to handle post data in meteor.js?

I need to handle some POST data in my meteor.js app, is there an easy way to do this?
Very basic, if it was a PHP app I would just want the $_POST variable.
Meteor router
https://github.com/tmeasday/meteor-router#server-side-routing
Meteor.Router.add('/items/:id', 'POST', function(id) {
// update Item Function
return [200, 'ok'];
});
If you are simply looking to intercept the GET and POST data, then send Meteor on it's merry way, you could do something like this on the server.
if (Meteor.isServer) {
var connect = Npm.require('connect');
var app = __meteor_bootstrap__.app;
var post, get;
app
// parse the POST data
.use(connect.bodyParser())
// parse the GET data
.use(connect.query())
// intercept data and send continue
.use(function(req, res, next) {
post = req.body;
get = req.query;
return next();
});
Meteor.startup(function() {
// do something with post and get variables
});
}
EDIT 11/01/13
I ended up creating a smart package for this (for myself). There is no documentation but you are welcome to use it. https://github.com/johnnyfreeman/request-data
To retrieve the foo request variable:
RequestData.get('foo') // --> 'bar'
RequestData.post('foo') // --> 'bar'
Both methods will throw a Meteor.Error if the key isn't found so make sure you use wrap with a try/catch if the variable is optional.
You can use Meteor's Iron Router, docs here, since Router (as mentioned above) is outdated and might be no longer functional.
Router.route('/items/:id', {where: 'server'})
.get(function () {
this.response.end('get request\n');
})
.post(function () {
this.response.end('post request\n');
});
I'm using this package to serialize body data: simple:json-routes. Here is the link.
And this code snippet to access it:
WebApp.connectHandlers.use('/api/request', (req, res, next) => {
console.log(req.body);
});

Categories

Resources