I'm messing around with creating an API with io.js and Express 4, and I have no idea why this isn't working. I am currently running the program on a DigitalOcean droplet (Ubuntu 14.04) and it is not calling the next() method never gets called/executed. The program is being routed to by a reverse proxy with nginx at https://<redacted>/asdf.
var express = require('express');
var app = express();
var port = process.env.PORT || 3000;
var router = express.Router();
router.use(function(req, res, next) {
console.log("Request received.");
next();
});
router.post('/login', function(req, res) {
console.log('Route /login accessed');
});
app.use('/', router);
app.listen(port);
console.log("Server started on port " + port + ".");
When I run the program on my droplet and send a POST request to https://<redacted>/asdf/login, the console prints out "Request received" but does not print out "Route /login accessed". The odd thing is that this works perfectly fine on my local machine when a post request is sent to http://localhost:3000/login. Is there something I am doing wrong or is this a known thing that I am not aware of?
Express uses the request's url property when mapping routes, and I suspect that your nginx reverse proxy isn't removing the /asdf root from it. If so, your url's path would be /asdf/login which would explain why your /login post handler isn't being invoked. To test this hypothesis you could try adding the reverse proxy root to your use like this:
app.use('/asdf', router);
If this is the case, to fix this problem, you can configure nginx to rewrite the url for you like this
location /asdf {
rewrite /asdf(.*) $1 break;
proxy_pass http://localhost:3200;
proxy_redirect off;
proxy_set_header Host $host;
}
More details:
https://serverfault.com/questions/379675/nginx-reverse-proxy-url-rewrite
http://wiki.nginx.org/HttpRewriteModule#rewrite
Related
I have a frontend www.myfrontend.tech and a backend www.mybackend.io. My previous developer managed to realize third-party authentications (like Microsoft) with PassportJS. But when looking at his code, I could not understand the flow and how the code enables this. Here is the code:
In the .conf of nginx of www.myfrontend.tech:
upstream mybackend {
server www.mybackend.io:443;
}
server {
location ~ /socialLoginSuccess {
rewrite ^ '/#/socialLoginSuccess' redirect;
}
location ~ /auth/(.*) {
proxy_pass https://mybackend/front/auth/$1?$query_string;
proxy_set_header Host www.myfrontend.tech;
}
... ...
}
In frontend/src/router.tsx of www.myfrontend.tech in ReactJS:
function RouterConfig({ history }: any) {
return (
<ConnectedRouter history={history}>
<Layout>
<Switch>
<Route path="/socialLoginSuccess">
<SocialLoginSuccess />
</Route>
... ...
In app.js of www.mybackend.io in NodeJS+ExpressJS+PassportJS:
var _front = require('./routes/front');
app.use('/front', _front);
In routes/front.js of www.mybackend.io:
router.get('/auth/microsoft', passport.authenticate('microsoft', { scope: ['User.Read'] }));
router.get('/auth/microsoft/callback', passport.authenticate('microsoft', {
successRedirect: '/auth/signinSuccess',
failureRedirect: '/auth/signinFailure',
failureFlash: true
}))
router.get('/auth/signinSuccess', function (req, res, next) {
res.redirect("/socialLoginSuccess");
})
In passport.module.js:
var Microsoft = (function() {
function Microsoft() {}
Microsoft.prototype.enable = function() {
var MicrosoftStrategy = require("passport-microsoft").Strategy;
passport.use(
"microsoft",
new MicrosoftStrategy(
{
clientID: keys.MICROSOFT_CLIENT_ID,
clientSecret: keys.MICROSOFT_CLIENT_SECRET,
callbackURL: `/auth/microsoft/callback`
},
this.loginSuccessfully
)
);
};
return Microsoft;
})();
During tests of this authentication, I can see in the address bar www.myfrontend.tech/auth/google and www.myfrontend.tech/#/socialLoginSuccess after signing in.
My question is, for example, where successRedirect: '/auth/signinSuccess' goes (backend or frontend)? and where does res.redirect("/socialLoginSuccess") go? How was it enabled by the code?
The flow:
User go to www.myfrontend.tech/auth/microsoft (forwarded to https://mybackend.io/front/auth/microsoft on the backend, since it matches location ~ /auth/(.*) on your nginx configuration)
It will redirect to Microsoft auth page
After user clicked authorize, it will redirected to the callback URL on your Microsoft Oauth App, which in your case is www.frontend.tech/auth/microsoft/callback (forwarded to https://mybackend.io/front/auth/microsoft/callback on the backend, since it matches location ~ /auth/(.*) on your nginx configuration)
On that callback, passport middleware checks if the authorization succeed or not, if it is, it will redirect to successRedirect URI, which is www.frontend.tech/auth/signinSuccess (forwarded to https://mybackend.io/front/auth/signinSuccess on the backend, since it matches location ~ /auth/(.*) on your nginx configuration)
Express app handle auth/signinSuccess success call, and redirects to /socialLoginSuccess, since /socialLoginSuccess location matches location ~ /socialLoginSuccess on your Nginx configuration (not location ~ /auth/(.*) therefor there's no proxy pass), it will be redirected to www.frontend.tech/#/socialLoginSuccess (by the rewrite ^ '/#/socialLoginSuccess' redirect;)
Since it's redirected to www.frontend.tech/#/socialLoginSuccess, it's now being handled by React router
And to answer your questions:
successRedirect will go to your front end, so it will be www.myfrontend.tech/auth/signinSuccess, but since you have proxy configuration on Nginx, and it matches location ~ /auth/(.*), it's being forwarded to www.mybackend.io/front/auth/signinSuccess (read more about reverse proxy)
res.redirect("/socialLoginSuccess") will redirect you to www.frontend.tech/#/socialLoginSuccess, the reason is mentioned on the 5th flow above
My Vue.js app switches from HTTPS to HTTP after the user logs in, and I'm not sure why. The Vue.js front-end is served using nginx, and uses Axios to access an Express back-end API that queries a local MongoDB server for login and a remote SQL Server for data. If someone could provide any guidance, that would be much appreciated, as this is for a work project.
I'm not sure if this could be a clue, but after logging into a session in the app, if I open a new tab and manually enter a URL for a view that doesn't access the Express back-end (https://subdomain.website.com/view_that_doesnt_access_express_api), the site will be secure under HTTPS, but if I do the same with a view that does access the Express back-end (https://subdomain.website.com/view_that_accesses_express_api), the site will be unsecure and the HTTPS in the location bar is shown crossed out in red.
nginx is configured with SSL and redirects 80 to 443 as follows (/etc/nginx/sites-available/project):
server {
listen 80;
server_name subdomain.website.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name _;
ssl_certificate /path/to/my/cert.crt;
ssl_certificate_key /path/to/my/key.rsa;
root /path/to/my/dist;
index index.html
access_log /var/log/nginx/site.access.log;
error_log /var/log/nginx/site.error.log;
location / {
try_files $uri /index.html =404;
}
}
The Express server is also configured to be HTTPS (app.js) with the same certification files used by nginx:
var httpscert = fs.readFile('cert.crt');
var httpskey = fs.readFile('key.rsa);
var options = {
key: httpskey,
cert: httpscert
};
var server = https.createServer(options, app);
server.listen(3000, function () {
console.log("Express HTTPS running on 3000");
})
Axios (Service.js):
import Api from '#/services/Api'
export default {
loginUser (email, password) {
return Api().post('login', {
email: email,
password: password
})
}
}
Axios (Api.js):
import axios from 'axios'
export default() => {
return axios.create({
baseURL: 'https://192.168.10.117:3000'
})
}
This is my first question on StackOverflow, so please let me know if I'm not asking this question correctly.
Thank you.
You said:
but if I do the same with a view that does access the Express back-end (https://subdomain.website.com/view_that_accesses_express_api), the site will be unsecure and the HTTPS in the location bar is shown crossed out in red.
That does not mean your setup is wrong, it just means you have on that page an asset that is served via HTTP, instead of HTTPS. And that's why the lock is crossed.
You should inspect your page source and check for any scripts or styles that are loaded via HTTP. Change those to be HTTPS and you should be good to go.
I'm trying to get my app on heroku to be 'https everywhere'. So far the app is like this:
"use strict";
console.log('working');
//Initial setup
var path, https, privateKey, certificate, port, cdjshelp, util, cookies, oauth, twitter, crypto, _, options, express, auth, lodash, dust, dustjs,
dustjsHelpers, commonDustjsHelpers, app, db, fs, mongoose, mongooseTimes, Comment, Bird, Sighting, Site, User,
Backbone, io;
//node modules, express and dust declarations
path = require('path');
util = require('util');
fs = require('fs');
https = require('https');
privateKey = fs.readFileSync('./config/privatekey.pem').toString();
certificate = fs.readFileSync('./config/certificate.pem').toString();
crypto = require('crypto');
//APP Defn...
app = require('./config/appSetup')(dustjs);
//******** SERVER CONFIG **********//
var port = process.env['PORT'] = process.env.PORT || 4000; // Used by https on localhost
options = {
key: privateKey,
cert: certificate
}
https.createServer(options, app).listen(port, function() {
console.log("Express server listening with https on port %d in %s mode", this.address().port, app.settings.env);
});
I've used the openSSL CLI to generate a privatekey.pem and a certificate.pem and loaded them as options.
I know that heroku has a procedure if you're using DNS records to have the app serve to your own domain. I know that you have to go through the procedure listed here. I'm not remapping any urls or altering any records - my url is birdsapp.heroku.com.
Heroku uses piggyback SSL, so if you setup an http server your app will respond to https requests without any additional config. The problem there is that the http routes are still available, so I've stuck to setting an https server only - but it's timing out with nothing in the logs, so I think that there's a problem with the SSL setup.
Is the above setup correct? Is that the best way to do basic https server on heroku?
OK, it's actually much simpler than that...
You simply create an http server:
//******** SERVER CONFIG **********//
var port = process.env['PORT'] = process.env.PORT || 4000;
http.createServer(app).listen(port, function() {
console.log("Express server listening with http on port %d in %s mode", this.address().port, app.settings.env);
});
and add a route redirect:
app.all('*', function(req, res, next) {
if (req.headers['x-forwarded-proto'] != 'https')
res.redirect('https://' + req.headers.host + req.url)
else
next() /* Continue to other routes if we're not redirecting */
});
heroku takes care of the rest, setting up an http server which is a mirror of your http server and uses their certs, etc.
This is a basic app I have got that uses express for setting up routes and and also does some query on a mongo db
If I go to http://localhost:8080/ whatever is in the views/hello.html will be displayed in the browser.
If i go to http://localhost:8080/test 'This is a test Page' will be displayed in the browser.
My question is why do I have to specify the port 8080 in the address? Or put another way how do I display what i want at this address http://localhost/ without specifying the port?
I know I can change the port by changing the value of 8080 here
app.listen(8080);
basic app below:
var express = require('express'),
app = express(),
cons = require('consolidate'),
crypto = require('crypto'),
MongoClient = require('mongodb').MongoClient;
app.engine('html', cons.swig);
app.set('view engine', 'html');
app.set('views', __dirname + '/views');
MongoClient.connect('mongodb://localhost:27017/m101', function(err, db) {
if(err) throw err;
//set up a route to go to the page http://localhost:8080/ to see 'This is a test Page'
app.get('/', function(req, res){
db.collection('hw1_3').findOne(function(err, doc) {
//do stuff here
return res.render('hello', { "name" : decrypted });
});
});
//set up a route to go to the page http://localhost/test to see 'This is a test Page'
app.get('/test', function(req, res){
return res.send('This is a test Page!!', 200);
});
app.listen(8080);
console.log('Express server started on port 8080');
});
The default port for http traffic is 80. If you bind to any port other than 80, you need to specify the port in the URL. app.listen(80) will take care of your problem.
On Unixy systems, root (administrator) access is required to bind to any port less than 1024, so you'll have to run your server like sudo node server.js to get port 80. You should bind to a higher port (like 8080) in this case while developing on your machine.
I'm pretty noob, but I'd say that localhost:8080 displays in place of www.somesite.com. I wouldn't get too wrapped up in the port number. If you deploy it to heroku or something you wont see it.
I’m trying to use Node.js to set up a proxy to Last.fm’s webservices. The problem is that every request to ws.audioscrobbler.com gets rewritten to www.last.fm. So for example $ curl http://localhost:8000/_api/test123 sends a 301 Moved Permanently to http://www.last.fm/test123.
var express = require('express'),
httpProxy = require('http-proxy');
// proxy server
var lastfmProxy = httpProxy.createServer(80, 'ws.audioscrobbler.com');
// target server
var app = express.createServer();
app.configure(function() {
app.use('/_api', lastfmProxy);
});
app.listen(8000);
At the same time $ curl http://ws.audioscrobbler.com/test123 returns a regular 404 Not Found. I’m not exactly sure what I’m missing here, or if I’m approaching this completely the wrong way.
The reason you get a 301 Moved Permanently is that ws.audioscrobbler.com gets an HTTP request with the hostname "localhost".
One solution is to let the proxy rewrite the hostname to "ws.audioscrobbler.com" before passing it on to the remote server:
var httpProxy = require('http-proxy');
var lastfmProxy = httpProxy.createServer(function (req, res, proxy) {
req.headers.host = 'ws.audioscrobbler.com';
proxy.proxyRequest(req, res, {
host: 'ws.audioscrobbler.com',
port: 80,
});
}).listen(8000);