Node JS : Allow only server side calls to my api - javascript

I have been racking my brains for a simple solution.
Lets say, I have 10 API endpoints in my Node JS application.
I have already allowed 3 of them to be public, the remaining 4 have JWT based authentication
Now I have 3 more routes, which will not have JWT and I need to only allow Server side calls. No browser or curl or postman, should be able to call them. How do I identify from the request object that it is originating from a server?
Or to put it in another way, how to I reject all cross origin calls to my api? As server side does not fall in CORS, they should filter through
----- EDIT -----
I recently came across a service that uses User Agent header to block server side calls.
Can i enforce User Agent header for my service and ensure that the header does not have browser agents. This can be hoodwinked easily, but as a theoretical solution, what would be the nodejs interceptor that discards requests whose user agent refers to a browser agent?

You can use the express-ipfilter package and only apply it to certain routes you want to protect:
const express = require('express'),
ipfilter = require('express-ipfilter').IpFilter;
// Whitelist the following IPs
const ips = ['127.0.0.1'];
// Create the route
app.get("/securePath", ipfilter(ips, {mode: 'allow'}), (req, res) => {
// only requests from 127.0.0.1 (localhost/loopback) can get here
});
app.get("/openPath", (req, res) => {
// all requests can get here
});
app.listen(3000);
If you are using Node behind a proxy, you may need to configure the proxy to set a header with the actual IP and then pass the ipfilter function a function in the detectIp property to the second parameter.
Let's say you are using nginx and have it configured to send the original IP through the x-Real-IP header, you can pass this function to ipfilter:
const express = require('express'),
ipfilter = require('express-ipfilter').IpFilter,
ips = ['127.0.0.1'];
app.get("/securePath", ipfilter(ips, {mode: 'allow', detectIp: getIp}), (req, res) => {
// only requests from 127.0.0.1 (localhost/loopback) that go through the proxy can get here.
});
app.get("/openPath", (req, res) => {
// all requests can get here
});
app.listen(3000);
function getIp(req) { return req.headers["X-Real-IP"] }

You should use a similar authentication/authorization as for the routes that have JWT authentication from the clients.
This means that the caller service should also authenticate using a JWT token, having a special role of service or something like that (this is 100% your decision on what convention you choose). That token should be signed by the caller and verified by the receiving microservice.
This solution has the advantage that it does not depends on the infrastructure, it works the same no matter where the services are deployed.

Related

How to authenticate ngrok password automatically using Python and or Javascript?

I am using ngrok to expose my localhost on a raspberry pi, and ngrok is password protected. I am using Django on another computer outside my network to access a webpage(simple server) on this raspberry pi. I don't want to manually type in the username and password to ngrok.
How can I automatically fill in the ngrok user/password, which is a popup in chrome asking for user and password?
What I've tried:
I first tried using JS in my template, just using a fetch request:
https://user:password#myngrokdomain.ngrok.io but chrome threw an error in the console saying I can't pass in credentials in plain text like this, rightfully so...
I then tried to use python requests:
UN= "myuser"
PWD = "mypassword"
loginURL = "https://myngrokdomain.ngrok.io"
client = requests.session()
login_data = dict(username=UN, password=PWD,)
r = client.post(loginURL, data=login_data)
This returned a 401 access denied
r.headers + r.reason returns:
401 Unauthorized Unauthorized {'Content-Length': '16', 'Content-Type': 'text/plain', 'Www-Authenticate': 'Basic realm="ngrok"', 'Date': 'Tue, 16 Mar 2021 15:22:15 GMT'}
The authentication method used by ngrok is called HTTP Basic Auth. To use it with the requests library you pass login and password as a tuple in the auth argument:
r = client.post(loginURL, auth=(UN, PWD))
Docs: Basic Authentication — Requests
Try doing a get on the login page first. Perhaps it's setting some cookies that it expects to be present on the post :
UN= "myuser"
PWD = "mypassword"
loginURL = "https://myngrokdomain.ngrok.io"
client = requests.session()
login_data = dict(username=UN, password=PWD,)
client.get(loginURL )
r = client.post(loginURL, data=login_data)
You need to distinguish whether you're making the request from a browser or from a server application.
Browser
If you're doing it from a browser you're hitting a CORS issue and ngrok doesn't support that when providing --auth. From the docs:
you cannot use ngrok's -auth option. ngrok's http tunnels allow you to specify basic authentication credentials to protect your tunnels. However, ngrok enforces this policy on all requests, including the preflight OPTIONS requests that are required by the CORS spec. In this case, your application must implement its own basic authentication
In this case, your only option is to implement authentication in your application instead of using ngrok's --auth.
Server
If you're sending the request from a server application you won't get into any CORS issue but you need to provide the Basic Authentication credentials properly.
Say you have your application exposed via ngrok at http://myapp.ngrok.io protected via --auth user:pass.
In plain Node.js you would do something like this:
const http = require('http')
http.get('http://myapp.ngrok.io', { auth: 'user:pass' }, res => {
const chunks = []
res.on('data', chunk => {
chunks.push(chunk)
})
res.on('end', () => {
console.log(Buffer.concat(chunks).toString('utf-8'))
})
})
Note that to hit the https url you would use Node's https module instead of http, or a higher level library that handles that for you, like node-fetch.
In Python you can do something similar and this question will probably get you on the right path.

Add Request Variable from Client to NodeJS Server for MiddleWare Router

I am trying to use routes to help server pages from a nodejs server based on a user being logged in. I am trying to take the login session details from the client side, and pass them back to the NodeJS server to be used in a middleware function for checking authentication.
How do I go about adding a variable to a request for a NodeJS route from the client side?
middleware.js
function loginRequired(req, res, next) {
if (!req.user) {
return res.status(401).render("unauthenticated");
}
next();
}
module.exports = { loginRequired };
server.js
app.use("/dashboard", middleware.loginRequired, mainPage);
The desired result would be, user clicks "Login", some variables are sent back, as well as the request to that route, the variables (i.e req.user) are used to check authentication.
You can send the variables in cookies, and use cookie parser, before your middleware. It provides parsed cookies.
For example, you can send a User variable in cookie, and then in your middleware use req.cookies which will contain your User variable.
Alternatively, you can send data in a POST request and use body-parser to do the same thing. But for authentication purpose, you should use cookies.

How to redirect user to login page when session token expired?

I have Marionette + Node application.
When token expires the app does not react and user is not redirects to the LogIn page.
My question is - how to listen for session token status from Node?
Good day to you sir. Let me give you a quick intro to request handlers, endpoints, and middleware.
Express.js is very common request handler. Request handlers, do what they sound like they do. They handle requests; more specifically http requests. You can find plenty of examples online on how to create basic endpoints with express.js.
Now on to the more important part, middleware. In express at least middleware is software that's inserted between the arriving request, and the end point it was meant to reach.
I will use Express syntax.
Say I have an endpoint foo:
Router.get('/foo', function(req, res) {});
However this endpoint should only be accessible under certain conditions. So I insert a middleware right in that request handler definition:
Router.get('/foo', function iAmAMiddleware(req, res, next) {
Here you can implement any logic you want. you have access to
the request, and the response. Meaning that if something in wrong
in the request, then you can return a response from here like
res.send(404);
BUT if all checks out all you have to do is call next() and
the flow will continue into the actual handler function.
},
function iAmTheEndpointHandler(req, res) {})
Usage of middleware is huge. Google express middleware and you'll find plenty of information.
Good luck to you.

Facebook login with Parse client site, use user object with Express js

I am trying to create a login procedure with the Parse and Facebook Javascript SDK. The authentication works without a problem on the client side, but I need to access the user object (created by Parse SDK) on the server side too. How can I do this the most elegant way? I thought when I log in into Facebook via Parse a cookie is set and so I can access the user object from the server. Or should I do the login process server side? Any recommendations?
I'm facing the same problem. Turns out that you can use either server-side auth or client-side auth. You cannot mix-and-match the two. Have a look at their official blog post about sessions.
var parseExpressCookieSession = require('parse-express-cookie-session');
// In your middleware setup...
app.use(express.cookieParser('YOUR_SIGNING_SECRET'));
app.use(parseExpressCookieSession({ cookie: { maxAge: 3600000 } }));
// Making a "login" endpoint is SOOOOOOOO easy.
app.post("/login", function(req, res) {
Parse.User.logIn(req.body.username, req.body.password).then(function() {
// Login succeeded, redirect to homepage.
// parseExpressCookieSession will automatically set cookie.
res.redirect('/');
},
function(error) {
// Login failed, redirect back to login form.
res.redirect("/login");
});
});
Also, I came across this when digging through the doc:
You can add Parse.User authentication and session management to your
Express app using the parseExpressCookieSession middleware. You just
need to call Parse.User.logIn() in Cloud Code, and this middleware
will automatically manage the user session for you.
You can use a web form to ask for the user's login credentials, and
log in the user in Cloud Code when you receive data from this form.
After you call Parse.User.logIn(), this middleware will automatically
set a cookie in the user's browser. During subsequent HTTP requests
from the same browser, this middleware will use this cookie to
automatically set the current user in Cloud Code.
...
When you work with user data, you should use HTTPS whenever possible.
To protect your app and your users, the parseExpressCookieSession
middleware requires you to use HTTPS. For your convenience, we also
provide a parseExpressHttpsRedirect middleware for redirecting all
HTTP requests to HTTPS.

Express.JS: Attach cookie to statically served content

I use Express.JS to serve static content:
express.use(express.static('./../'));
When index.html is served, I would like to send a cookie alongside the response indicating if the user is signed in or not. Normally one should use res.cookie() but I cannot see how to use it for statically served content.
Not sure why you need to do this, but you can place your own middleware before the static module.
The following quick hack should work:
function attach_cookie(url, cookie, value) {
return function(req, res, next) {
if (req.url == url) {
res.cookie(cookie, value);
}
next();
}
}
app.configure(function(){
app.use(attach_cookie('/index.html', 'mycookie', 'value'));
app.use(express.static(path.join(__dirname, 'public')));
});
The above inserts another function in the chain of middleware express uses before the static middleware. The new layer attaches the cookie to the response if the URL matches the specific one you are looking for -- and passes the response further down the chain.
Consider also the following approach:
If your express is behind web-server you can serve static files without bothering express - it should be faster than via middle-ware. If you use nginx, this can help: Nginx Reverse Proxying to Node.js with Rewrite.
Assuming that your static file has javascript in it, you can also set cookies directly on the client side only requesting from express the data you need for this cookie:
document.cookie = "user_id=" + user_id;
Flanagan's JS definitive guide (edition 6!) has an excellent coverage on how to use cookies in client-side javascript (in addition to being the best among JavaScript books :).
It can be a trivial advice, but I have seen the following flow (more than once): client sends API request (which has a cookie attached to it, obviously), server gets data from the cookie and serves the response completely built on the data contained in this cookie. All this instead of just quietly reading this cookie in the client. Basically client asks the server what it has in its own cookie.
In your scenario you need to request user_id/access_key only once and then always check the cookie in the client, going to the server only for the data that client doesn't already have, but storing and checking session state and, maybe, some compact data used in most pages (such as username, e.g.) in cookies locally (you can also cache data in local storage, to reduce the server load even further). In this case, express won't even know if a user accidentally refreshes the page (if you don't change URLs to reflect application state as well, of course, or only change #-part).
As app.configure was removed since Express.js v4, I would like to do an update.
For Express.js versions > 4, to initialise my app by passing options inside the express.static(root, [options]) method, in which you can pass a Set-Cookie header in the property called setHeaders:
app.use(express.static(publicPath, {
setHeaders: function (res, path, stat) {
res.set('Set-Cookie', "myCookie=cookieValue;Path=/")
}
}));
It is important to set Path=/ because otherwise express will create numerous duplicates of the cookie on the client side.

Categories

Resources