Express block/allow some nested endpoints - javascript

My application has a guest mode. I want to block access to endpoints that need signing in.
Lets say I have the following snippet:
app.use('/api/user', authRoutes);
// profile route
router.get('/:userId', viewAction); // <- visits profile, allowed for guests
router.get('/:userId/follow', followAction); // <- follow profile, not allowed for guests
Now I could just check inside followAction if the user is a guest or not, but I want to do it the other way around. So I want to block all routes except for ones which have specified they allow guests. (so I don't accidentally forget to check for auth)
I was thinking of something like an array of allowed endpoints for a guest, but this doesn't work since the 'dynamic' endpoints won't match.
const guestEndpoints = ['/user/i_cant_just_put_a_userId_here']; // <- missing :userId here.
Is it possible to do something like this?
router.get('/:userId', viewAction, allowedForGuests);
I think this should be quite easy to do, am I missing something?

I would suggest looking into a 'jwt' library and adding a middleware for those specific protected routes. JWT (json web token) works by sending the client a token when they log in that is then cached on the client side and requiring that token to be sent with each request to access protected routes. The token is usually set in the headers.
Here is a good article to get started on middleware...
http://expressjs.com/en/guide/using-middleware.html
And here is the jwt npm module that I prefer...
https://www.npmjs.com/package/jsonwebtoken

Related

Authenticate requests from frontend that doesn't have tokens

Not sure if the title summarises my question well.
Basically, I am trying to authenticate routes such as checking if user exists etc. I only want to allow
requests coming from my frontend application to be approved, but, since no user is signed in there is no token to send.
Api request -
mywebiste/checkUser/email
This route is unprotected on my backend because no user is logged in.
BUT I want to protect this route, in such a way that it's accessible only from the frontend.
Some ideas I came up with were adding specific headers tag from the frontend and check them on the backend, but that could be easily replicated, is there something more secure like using tokens etc.
I am using React and Node.js
Same origin policy is going to give you some basic protection, but basically if an API endpoint is exposed publicly, it's exposed publicly. If you don't want that route to be publicly accessible you need to add access control.
If you use that route to check if a user is already registered, you could, for example, merge it with the user registration route and send a different error code if the user already exists (which is not a great idea because it leaks which emails are registered on your system).
You can verify that a request was originated by a user (by authenticating him) but you cannot verify that a request comes from a particular client because of these two reasons :
If you include some API key in your client (web page or other), it's easily retrievable by everyone (the best thing you could do is offuscate it which makes things slightly harder but still possible)
If you send an API key over the network it's easily retrievable as well
The only thing you could do is prevent other web pages from calling your backend on behalf of the user, by using CORS (which is actually active by default if you dont specify an Access-Control-Allow-Origin header)
I ended up creating a kind of working solution, so basically, I create a new base64 string on my frontend and attach that to the header while making a request to the backend. The base64 string is different every minute, so even if the header is copied, it differs every minute and is combined with your secret key.
I have made a package so that people can use it if they want - https://github.com/dhiraj1site/ncrypter
You can use it like so
var ncrypter = require('ncrypter');
//use encode on your frontend with number of seconds and secret key
var encodedString = ncrypter.encrypt(2, 'mysecret1')
//use decode on your backend with same seconds and secret
var decodedString = ncrypter.decrypt(encodedString, 2, 'mysecret1');
console.log('permission granted -->', decodedString);

In Node.js using Express, restrict access to a route based on user credentials

In express app where users login using their web identities (google, facebook, amazon, etc) using passport.js for this. I have created a route. but only want that a single user should have access to it.
I have it working with extensive testing, but not entirely sure if its in fact secure enough?
Here is my code for the route:
app.get("/superSecretPage", function(req, res){
console.log(req.user);
if (req.isAuthenticated()){
if (req.user.userId === "XXXXXXXXXXXXXXXX") {
User.find({}, function(err, users){
res.render("userlist", {
users: users,
});
});
} else {
res.render("invalid")
}
} else {
res.redirect("login")
}
});
XXXXXXXXXXXXXXXX = being the users uniqueID from Google/Amazon/Facebook or another ID provider
Like i said this work, and only allows the user that equals req.user.userId, but is this secure enough?
This is not a proper way to do that. What will you do If you want to allow another user later? Will you open code and edit the user Id?
Do not hard code the user ID.
You need to implement Role Base Access Control. You need to create a super power user role and assign it to the users, Who you want to allow to access your super secret page. And use middlewares to check the role permissions.
This will help you : Role based auth | NodeJS
Like i said this work, and only allows the user that equals req.user.userId, but is this secure enough?
You haven't really shown enough to know about the overall security of this. The security depends upon at least the following:
Implementation of req.isAuthenticated()
How the user session was established and protected. Most sessions use a browser cookie to identify the session and it must be run over https to offer any protection. Then, you must also have proper auto-logout to make it less likely the session is just left active where someone else can use it on that computer.
How the req.user.userId got set in the first place. You don't show this so we really have no idea what is behind that and whether it's following good security practices or not.
If there is any persistent storage of the user session, then the security of that storage mechanism is relevant.
Presuming there's a login mechanism, are secure (hard to guess) credentials required? Are there anti-hacking protections in place to attempt to thwart credential guessing.
If you are 100% sure that req.user.userId is valid and that this connection belongs to the appropriate user for the userId, then this bit of code works fine. But, the devil is in the details of making sure that req.user.userId is appropriately connected to an authorized user and that's where the hard work is of making security work.
Add a field in the db schema and set it to an array which contains all the route that the user has access to. When authenticating loop through the array to see if he/she can access the route he/she is trying to.

How can we separate route of admin and user?

I am creating routes in node js . I am creating routes for dashboard.
User login and get the JWT token.
By sending Token,User can access some route related to user(edit,delete,logout route etc).
But For admin, I want to create the routes which can see the list of users,edit or remove users,check the logout time of users.I have also set the flag in table to identify the person is user or Admin.
How will be authenticate routes for Admins on backend side?
You can be inspired by this logic, And no further explanation can be given here. follow steps (It may help):
First) define role field into DB mongoDB or Mysql (for example):
enum: ['user', 'admin']
Second) create a function checkRole(role) for check role after signin and verify jwt, then get user
Third) create separate route for admin panel (for example):
router.route('/admin-panel').use(authController.checkRole('admin'))
You can put your authorization flag in your JWT. When a user logs in, your server generates corresponding JWT, in which included authentication info(i.e. userId). You can put additional authorization info in the token(i.e. auth).
Based on the auth field, your server can identify whether the request is sent by a general user or an admin. Of course, securing the JWT from hijacking is an another story.

How can one route a search URL which has a query string embedded?

I am working on an express application and I have two GET URLs. One fetches all resources in the database but is right-protected(needs authentication and admin access) while the other fetches resources based on a search parameter(query string).
The route that requires authentication looks like so:
carRouter.get('/car', verifyToken, isAdmin, fetchAllCarAds);
This means that the admin has to be logged in first and then a check is carried out to ascertain whether he is truly an admin before he is given access. Now I have another GET route that doesn't require authentication like so
:
carRouter.get('/car?status=unsold', filterUnsoldCars);.
I understand that express does not allow routing based on query strings so how do I ensure that the request that does not require authentication(query string) is accessible in the one without query string?
You can do the following things to make it work.
Check either query string exist or not inside isAdmin middleware
If query string exists, Skip validation that has been implemented inside middleware.
If query string doesn't exist, then check either user is an admin or not.
I fixed it by placing it before the verifyToken function and it worked. I would like to learn of other ways of doing it if there are. Thanks
carRouter.get('/car', filterUnsoldCars, verifyToken, isAdminDummy, fetchAllCarAds);
I dont think you are building a proper routing but, you can use a middleware to check query params and bypasses to the next route assigned. check below routing explanation for next('route')
Express Routing
carRouter.get('/car', hasParams, verifyToken, isAdminDummy, fetchAllCarAds);
carRouter.get('/car?status=unsold', filterUnsoldCars);
if hasParams true, next('route')
if hasParams false, next()

Get an access token without being connected to facebook

I need to retrieve a facebook page's list of posts (feed) using their javascript SDK, just like they explain in their docs: https://developers.facebook.com/docs/graph-api/reference/v2.4/page/feed
/* make the API call */
FB.api(
"/{page-id}/posts",
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);
I need it to be my website's "news section", so users should see it even if they are not connected to facebook.
The problem
Cool, but there is a problem... It returns: An access token is required to request this resource.
Holy cow... I'd like to get some access token for you #facebook, but my app doesn't make use of your authentication tools/plugins.
ANYWAY, I tried with FB.getLoginStatus(); but doesn't work, because the only way it can return an access_token is if the user is actually connected to the application. My users may not even be logged to facebook!
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
I've already payed a look to this SO question, but it doesn't solves my problem, simply because there are no such "generic tokens".
I've also read https://developers.facebook.com/docs/facebook-login/access-tokens/ and that also relies on tokens generated through facebook login methods... So, can't I display a list of fb page's posts in my website, without being connected into facebook, hence an application?
ADD: My app is build with angularJS, I'm not dealing with server-side code. I shall rely purely on javascript methods.
You could either use an page or an app access token, but as you'd be using them on the client-side, neither of them are an option.
See
https://developers.facebook.com/docs/facebook-login/access-tokens#apptokens
https://developers.facebook.com/docs/facebook-login/access-tokens#pagetokens
Note that because this request uses your app secret, it must never be made in client-side code or in an app binary that could be decompiled. It is important that your app secret is never shared with anyone. Therefore, this API call should only be made using server-side code.
I'd strongly recommend to build a simple server-side script (PHP for example) to proxy the request to the Graph API. You could then call this via AJAX for example and load the posts asynchronously (and alse get rid of the FB JS SDK!). There is NO way to handle this in a secure manner if you don't want to use FB Login for all of your users (which also doesn't make much sense IMHO).
I think it's straightforward :)
Since pages' posts are always public, it only needs a valid access token to retrieve page posts.
Quoting what you've written:
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
You only require an access token.
My suggestion would be;
- Generate an access token for yourself (no extra permission needed)
- request page-id/posts
This way you don't require other users to be connected to facebook, you can simply requests page-id/posts to retrieve posts with access token you generated for yourself.
I hope it solves your problem :D
TIP: As long as posts are public, you only require a valid access token, it doesn't need to be user or page specific.

Categories