Hello and thank you in advance.
We are running our Node.js/Express application in App Engine which serves a React front end as static files. At one point the user is able to redirect to a third-party so that the user can sign in and authorize some of their data which is managed by that third-party. We provide the redirect URI to the third-party as a parameter in the querystring, which is https://www.example.com/api/thirdparty/OAuthCallback.
When the user submits the authorization on the third-party side, they should be redirected to that URI, which is an API endpoint in our application where we process the result, and then in our backend we redirect the user to their dashboard. Instead, the user gets redirected to that URI in the browser.
We know (we think) we are doing the routing correctly because we are able to hit that end point from Postman and simulate the process that way.
The question is, why is this redirection going to the browser and not our backend?
Below you will find our server entry point (index.js) along with the route we expect to hit (thirdparty.js)
index.js:
//index.js
import express from "express";
import thirdparty from "./routes/thirdparty";
import httpsRedirect from "./middlewares/https-redirect";
const app = express();
var httpsPort = app.get('https-port');
app.use(httpsRedirect({httpsPort: httpsPort}));
app.set('trust proxy', true);
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.use(express.static(path.join(__dirname, '../build')));
app.use("/api/thirdparty", thirdparty);
app.get("/*", (req, res, next) => {
console.log('new request from: '+req);
res.sendFile(path.join(__dirname, '../build', 'index.html'));
});
app.listen(8080, ()=>console.log('Listening on port 8080'));
thirdparty.js (route):
// thirdparty.js
import express from "express";
const router = express.Router();
var oauth2 = require('./lib/OAuth2.0')();
router.get('/OAuthCallback', function (req, res) {
console.log("CODE--------------------------- : " + req.query.code);
var params = {
code: req.query.code,
redirect_uri: 'https://www.example.com/api/thirdparty/OAuthCallback'
}
oauth2.authCode.getOAuthToken(params, saveToken);
function saveToken(error, result) {
// do things with the result
res.redirect(process.env.HOST+"/dashboard");
}
});
export default router;
Related
I'm building a react app with an express server, and using OAuth 2 to securely access intuit APIs. The express server sends the authorization request, and then receives the authorization code at localhost:8000/callback where it's converted into an access token. The problem is that this access token is only accessible in the express app - so how do I send it to my react front end, so I can use it to securely make REST calls from react?
I've look at similar questions and there seems to be two solutions: use JWT, or store the access token on local storage. I'm just not sure where to start with either of these approaches, since I'm new to learning auth.
Anyway, my app structured so that my react app runs off a dev server at localhost:3000, and proxies request to my express server at localhost:8000.
Here's the express app:
'use strict';
require('dotenv').config();
/**
* Require the dependencies
* #type {*|createApplication}
*/
var express = require('express');
var app = express();
var path = require('path');
var OAuthClient = require('intuit-oauth');
var bodyParser = require('body-parser');
var ngrok = (process.env.NGROK_ENABLED==="true") ? require('ngrok'):null;
/**
* Configure View and Handlebars
*/
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.static(path.join(__dirname, '/public')));
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(bodyParser.json())
var urlencodedParser = bodyParser.urlencoded({ extended: false });
/**
* App Variables
* #type {null}
*/
var oauth2_token_json = null,
redirectUri = '';
/**
* Instantiate new Client
* #type {OAuthClient}
*/
var oauthClient = null;
/**
* Home Route
*/
app.get('/', function(req, res) {
res.render('index');
});
/**
* Get the AuthorizeUri
*/
app.get('/authUri', urlencodedParser, function(req,res) {
oauthClient = new OAuthClient({
clientId: '*****',
clientSecret: '*****',
environment: 'sandbox',
redirectUri: 'http://localhost:8000/callback'
});
var authUri = oauthClient.authorizeUri({scope:[OAuthClient.scopes.Accounting],state:'intuit-test'});
res.send(authUri);
});
/**
* Handle the callback to extract the `Auth Code` and exchange them for `Bearer-Tokens`
*/
app.get('/callback', function(req, res) {
oauthClient.createToken(req.url)
.then(function(authResponse) {
oauth2_token_json = JSON.stringify(authResponse.getJson(), null,2);
})
.catch(function(e) {
console.error(e);
});
res.redirect('http://localhost:3000')
});
/**
* getCompanyInfo ()
*/
app.get('/getCompanyInfo', function(req,res){
var companyID = oauthClient.getToken().realmId;
var url = oauthClient.environment == 'sandbox' ? OAuthClient.environment.sandbox : OAuthClient.environment.production ;
oauthClient.makeApiCall({url: url + 'v3/company/' + companyID +'/companyinfo/' + companyID})
.then(function(authResponse){
console.log("The response for API call is :"+JSON.stringify(authResponse));
res.send(JSON.parse(authResponse.text()));
})
.catch(function(e) {
console.error(e);
});
});
app.get('/test', (req,res) => {
res.send('hello from server')
})
const server = app.listen(process.env.PORT || 8000, () => {
console.log(`💻 Server listening on port ${server.address().port}`);
})
The /authUri request is made by the browser via the proxy server, and its response is a redirect to the callback URL. Therefore, the callback URL must also be on the frontend server (i.e., http://localhost:3000/callback). The proxy server will then forward the /callback request to the express server, and the express server can put the access token in an (HTTP only) cookie that is sent back (via the proxy server) to the browser.
app.get('/callback', function(req, res) {
oauthClient.createToken(req.url)
.then(function(authResponse) {
res.cookie("session", authResponse.getJson().access_token,
{path: "/", httpOnly: true});
res.redirect('http://localhost:3000');
});
});
Verify that the proxy server preserves both sent and received cookies.
Then with every subsequent request, the browser will send the cookie (via the proxy server) back to the express server.
(Unlike the local storage, an HTTP only cookie is not accessible via JavaScript, which gives extra protection against cross-site scripting attacks.)
I'm using http-proxy-middleware (and open to suggestions, but would like to stick to the modules which are proxying requests instead of creating new ones like request or http) to proxy requests to remote host.
Two problems I'm not seeing solutions to currenty:
1) I have a CSRF-protected form (via csurf). I would like it that the middleware checks the CSRF-token first, and in case it is valid only then proxies the request to another host, obtains the response and sends it to user. How to achieve such a setup?
2) http-proxy-middleware (and some other proxying modules) utilizes app.use to set one forwarding rule (append the route to the host), however I would like to have a more fine-grained control over routes - each of my routes must have its own endpoint on the remote host.
The code:
const express = require('express')
const csrf = require('csurf')
const cookieParser = require('cookie-parser')
const proxy = require('http-proxy-middleware')
var app = express()
var csrfProtection = csrf({ cookie: true })
app.use(cookieParser())
// not quite what I need, since different
// routes would utilize different endpoints
app.use('/api', proxy('http://example.com'))
app.get('/forms', (req, res) => {
res.send(
res.render('csrf-protected-forms.html', { csrfToken: req.csrfToken() })
)
})
app.post('/api/one', csrfProtection, (req, res) => {
response = // proxies to 'http://example.com/something/one'
// and obtains response
res.send(response)
})
app.post('/api/two', csrfProtection, (req, res) => {
response = // proxies to 'http://example.com/somethingelse/and/here/two'
// and obtains response
res.send(response)
})
app.listen(3000)
In your code csrf protection runs after proxied middleware. In case if you want protect only this two routes '/api/one','/api/two':
app.use(['/api/one','/api/two'], csrfProtection, proxy('http://example.com'))
app.use('/api', proxy('http://example.com'))
Or if you want protect all POST requests to API, you need somthing this:
app.use('/api', csrfProtection, proxy('http://example.com'))
I am working on a nodeJS application. So far i've learned you can protect routes with JWT and i've implemented this. The problem is, I am not sure how to protect a route where the user is allowed to post to.
Let's take a register route for example, this route will be used for user registration. I want users to be able to post to this, BUT only from my application. I dont want anyone to just be able to connect to it via postman and post whatever. How do you protect these kind of routes.
Thanks in advance.
You can use CORS middleware to allow only specific clients to access your server https://expressjs.com/en/resources/middleware/cors.html
Example:
var express = require('express')
var cors = require('cors')
var app = express()
var corsOptions = {
origin: 'http://example.com',
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for only example.com.'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
i just want to try basic methods to set cookie and show in request head.
front-end is just a basic html form with username and password text input, use POST method to transfer data.
below is code based on express.
server just receive req.body, then set it as cookie with domain: localhost:1338/base
cookieRouter.js:
var express = require('express');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var router = express.Router();
router.use(bodyParser.urlencoded({ extended: false }));
router.use(cookieParser());
router.get('/', function (req, res) {
res.send('this is a router base page!');
});
router.get('/index1.html', function (req, res) {
res.sendFile(__dirname + '/index1.html');
});
router.post('/index1.html', function (req, res) {
res.cookie('name', req.body, { domain: 'localhost:1338', path: '/base' });
res.send(req.body);
});
module.exports = router;
app.js
var express = require('express');
var app = express();
var cookieRouter = require('./cookieRouter.js');
app.get('/', function (req, res) {
res.send('this is home page!');
});
app.use('/base', cookieRouter);
app.listen(1338);
after run app.js, request header has set-cookie value obviously. but can't get it into request header, and req.cookies is empty object {}, even after refreshing the web.
but if i just use simplest demo, it can work, for instance:
router.get('/', function (req, res) {
res.cookie('name', 'test');
});
one more thing, i feel the trouble with express is that only one res.send(), res.redirect()... can be sent as by default it will add head automatically, otherwise, it will come up with error: Can't set headers after they are sent.
someone said add return can solve this problem, but i failed, so want to how how to add, can anyone give an complete demo?
The cookie is missing because the domain attribute is incorrect -- 'localhost:1338' need to be changed to 'localhost'. Port information should not be included in domain.
Yes, according to the Network panel of browser dev tool, there is a Set-Cookie response header (as the screenshot displayed). However, if you check Application - Cookies panel in Chrome (or corresponding panel in other browsers), you will find that: the cookie specified by Set-Cookie header is not there. Browser does not store it and won't send it in the following HTTP requests.
Also, please note that as the cookie's path is /base, only the HTTP requests whose URL starts with /base can send the cookie.
I am building an app with express js which will have different clients like web and mobile. I didnt want to use one app for both as some middleware would be additional burden. For say like session middleware. So is it possible for one project to have two apps. And how would it work?
The app object that you make in express is a function(req,res,next) that is suitable for Express's own middleware chains. So you can use app.use to send requests matching a leading path fragment to an app defined elsewhere.
Docs: http://expressjs.com/api.html#app.use
$ npm install express
//mobile.js
var app = require('express')();
app.get('/', function(req, res){
res.send('Mobile Route')
});
module.exports = app;
//desktopApp.js
var http = require('http');
var express = require('express');
var desktopApp = express();
var mobileApp = require('./mobile.js');
desktopApp.use('/mobile', mobileApp)
desktopApp.use(desktopApp.router);
desktopApp.use(express.errorHandler());
desktopApp.get('/', function(req, res){
res.send('Desktop Route')
});
desktopApp.get('/mobile', function(req, res){
// Because Express respects the order that you set up the middleware chain,
// the mobileApp `/mobile` route gets first dibs to send a response or next()
res.send('Inaccessible Desktop Route')
});
desktopApp.get('/mobile/foobar', function(req, res){
// When mobileApp can't find any suitable route matching this path, it gives
// up, and desktopApp continues to pass the request down the middleware stack.
// It ends up matching this route, where we send a response
res.send('Desktop Route')
});
http.createServer(desktopApp).listen(3000, function(){
console.log('Listening on 3000');
});
// Results
$ curl localhost:3000/
Desktop Route
$ curl localhost:3000/mobile/
Mobile Route
See the vhost example on the express github repository.
You can have a "main" app, which routes the requests to one app or another. You should write a middleware to establish the conditions where one app or another are requested. express.vhost is a good example, but maybe you need other checks than the domain one.
main-app.js
(The file called to start the server.)
// load dependencies
var main = express();
main.use( express.vhost( 'mobile', require( './the-mobile-app' ) );
main.use( express.vhost( '*', require( './the-web-app' ) );
main.listen( /*...*/ )
the-mobile-app and the-web-app.js
var app = express();
//
// setup your application conf, middleware, and routes
//
module.exports = app;
I wanted to share a different approach that I used in a project recently:
function renderAppropriate(template1, template2){
return function(req, res){
if(req.session && req.session.mobileOn){
res.render(template1);
} else {
res.render(template2);
}
};
};
app.get('/', function(req, res, next){
// do some stuff
next()
}, renderAppropriate('someMobileTemplate', 'someDesktopTemplate')
);