Right now, when I visit my page at https://example.com and click login, it goes to https://example.com/auth/facebook, which then does the facebook stuff and ends up calling back http://example.com/auth/facebook/callback. I can't seem to get it to call back using the https scheme (but only when the request cycle started in https).
right now, when viewed through an https iframe (facebook canvas app), I get the error
[blocked] The page at
'https://apps.facebook.com/example/?fb_source=notification&ref=notif¬if_t=app_notification'
was loaded over HTTPS, but ran insecure content from
'http://example.com/auth/facebook/callback?code=AQD5TUeTP…yXC0ZM8S45V2iTta629IaquCpAqVUbhAvNCFveaDBlbKg4J4#=':
this content should also be loaded over HTTPS.
passport.use(new FacebookStrategy({
clientID: process.env.FB_CLIENT,
clientSecret: process.env.FB_SECRET,
callbackURL: "/auth/facebook/callback",
profileFields: ['id']
},...
app.get('/auth/facebook',
passport.authenticate('facebook', {
scope: ["read_stream"]
})
);
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
failureRedirect: '/#'
}),
function(req, res) {
res.redirect('/#');
});
I'm running this on heroku, where it handles the details on https.
EDIT
Apparently node provides req.connection.encrypted with information as to whether the request is https. Since I am running on heroku behind nginx where that handles all of the https before node, req.connection.encrypted will always be undefined.
Still don't know how to solve this though.
I looked into the Passport Oauth2 strategy code and checked that it uses req.connection.encrypted to check if it is in a secure connection.
It also checks for proxies in case the server code runs behind one. It is possible to tell passport to trust a proxy if you know that you are behind one.
It seems that since SSL is handled by nginx on Heroku, req.connection.encrypted is always "undefined". (groups.google.com/forum/#!topic/express-js/Bm6yozgoDSY)
Nginx handles all of the HTTPS on Heroku so node never sees req.connection.encrypted being anything other than "undefined".
To solve the problem you have to tell passport to trust the proxy adding the line
app.enable("trust proxy");
to your express server.
I've also learned that we can accomplish the same thing by adding another property called "proxy:true" to the googleStrategy such as below:
passport.use(new GoogleStrategy({
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
callbackURL: '/auth/google/callback',
proxy: true }
Nginx handles all of the HTTPS on Heroku so node never sees req.connection.encrypted being anything other than undefined. By digging through the passportjs repositories I found that there is a check for the app to have "trust proxy" enabled. To solve this problem, add the line
app.enable("trust proxy");
to your express server.
I am sorry, this answer is kind of lame, I cannot really give you a specific answer based on the nature of the thing.
I believe that your callback url in the facebook api console is probably http, if you change it to https it should work.
If that does not work, you could try changing your callbackURL to a full url ("https://example.com/auth/facebook/callback")
Trying passport-facebook and passport-disqus I had the same problem, with Google there's no problem since it let you use http for callback but the others don't.
Setting "app.enable("trust proxy");" on main app file solved it!
Related
I've been searching lots and lots of sources but does not seem like anything describes my case.
I am currently trying to develop a website (ReactJS) and a back-end (NodeJS w/ Express). Let's say they are accessible at example.com and example.com/api respectively on production, and then localhost and localhost:3030 on development. The cookies are set by the API.
Entering alert(document.cookie) into my browser's console on my local machine outputs my non-HTTPOnly cookies. However, when I tried this on my web hosting, it only returns an empty alert box, which indicates that document.cookie is an empty string "". This was tested on Opera, Firefox and Chrome.
I also have already tried unsetting HTTPOnly and secure flag for my session cookies from my Node app but this too is not written into the alert output.
Does anyone here know why this happens and/or how to make this work? Thanks!
EDIT: Here's how I set my cookie on the NodeJS
app.use(function (req, res, next) {
res.cookie("My-Cookie", random());
next();
})
EDIT2: I guess this is an issue with how CORS actually work. But I still don't get why my localhost version works even though it's also CORS.
Turns out this issue is regarding the Set-Cookie domain. First, my cookie was set for www.example.com while I open example.com in my browser. Second, I fixed this problem by specifying {domain} option on ALL my cookie settings including the cookie generator like so:
app.use(function (req, res, next) {
res.cookie("My-Cookie", random(), { domain: '.example.com' });
next();
});
Now I can open either www.example.com or example.com and document.cookie returns the cookie values as intended.
Hope this helps anyone out there who are not aware of this limitation.
I was needed to redirect http to https and found this code:
app.enable('trust proxy');
app.use((req, res, next) => {
if (req.secure) {
next();
} else {
res.redirect('https://' + req.headers.host + req.url);
}
});
I'm using heroku to host my project, I noticed that heroku as default issued *.herokuapp.com cert, so I can use http and https as well.
When looked at req.secure within app.use callback, without app.enable('trust proxy'), req.secure is always false, when I add app.enable('trust proxy') it's false for about 2 times and after the https redirection it's switches to true.
app.enable('trust proxy'), the docs:
Indicates the app is behind a front-facing proxy, and to use the
X-Forwarded-* headers to determine the connection and the IP address
of the client.
My question:
Why would my server be behind a proxy?(is it relates to the issued *.herokuapp.com cert?), if someone could explain how all fits together, I mean, why my server is behind a proxy? and why without app.enable express won't identify(or accept) secure connection?
If your not running behind a proxy, it's not required. Eg, if your running multiple websites on a server, chances are your using a Proxy.
X-Forwarded-For header attributes get added when doing this so that your proxy can see what the original url was, proxying in the end will be going to localhost you see. The reason why it's needed is that X-Forwared-For can be faked, there is nothing stopping the client adding these too, not just a proxy. So trust-proxy should only be enabled on the receiving end, that would be behind your firewall. Because you have control, you can trust this.
So in a nutshell, if your website is running behind a proxy, you can enable it. If you website is running direct on port 80, you don't want to trust it. As the sender could pretend to be coming from localhost etc.
What is the way to configure sails.js to set secure cookies? We are using redis to persist session state. The sails.js prescribed way (rather than some Express middleware option) is desired. Ultimately, I want the "secure" column in the Chrome cookies view to be checked for the app's cookie:
In the docs, there is no explicit mention of how to do this:
http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.session.html
There is an ssl config option, but deploying the app with ssl: true did not produce the desired result:
module.exports.session = {
...
ssl: true
...
}
The ssl option isn't documented either, but I assume it has something to do with signing cookies instead.
edit: in the screen shot, I'm serving from localhost without HTTPS, but this app is being served from a production server using HTTPS and the same behavior is observed
Sails uses express.session to handle session cookies, therefore you can enable secure cookies by setting cookie: { secure: true } in config/session.js
You need to use HTTPS for express to set the cookie
it requires an https-enabled website, i.e., HTTPS is necessary for secure
cookies. If secure is set, and you access your site over HTTP,
the cookie will not be set.
If you are behind a proxy that does SSL termination on behalf of your web server enable express trust proxy option by adding the following middleware in config/http.js
module.exports.http = {
customMiddleware: function(app) {
app.enable('trust proxy');
}
};
It appears that there is not a way to do this currently. If you look at the sails.js session implementation here (https://github.com/balderdashy/sails/blob/98522d0bc5df5e6bc30b4dc35708ae71cf4625e2/lib/hooks/session/index.js) you'll see that there is, in fact, no secure-mode stuff whatsoever :(
Since sails is using their own session store implementation, and not piggybacking off of node-client-sessions or express-sessions, the only way to solve this (I think) would be to submit a PR to the sails people.
Sorry!
You can set signed cookies like so
Adding a signed cookie named "chocolatechip" with value "Yummy:
res.cookie('chocolatechip', 'Yummy', {signed:true});
Retrieving the cookie:
req.signedCookies.chocolatechip; //"Yummy"
check out the sails Documentation
I'm working on a simple web app running Node/Express on the server using Passport to authenticate via Google, etc. The client is using Angular.
I'm unable to get Angular to play nice with Passport to log into Google though. My setup is like below:
Node/Express/Passport REST endpoint:
app.get("/auth/google",
function(req, res, next) {
passport = req._passport.instance;
passport.authenticate("google", { scope: ['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'] })(req, res, next);
});
The client side...
myModule.controller("myController", function($q, $scope, $http) {
$scope.loginWithGoogle = function() {
$http.get("myDomain/auth/google").then(function(response) {
doStuff();
});
}
...called by a simple:
<button ng-click="loginWithGoogle ()">Login</button>
The error I'm getting is 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'myDomain' is therefore not allowed access.'
However, if I access the REST endpoint directly, by going to myDomain/auth/google in the browser, it works fine. Or I can make a simple HTML Login and get it to work too. So I'm guessing something is going wrong in the wire between my client and server.
I've searched for solutions and could not find any that worked. For instance, I read a suggestion to use $http.jsonp instead of $http.get. That yielded some progress - if a user is already logged into Google (via gmail, other apps, etc.) then they authenticate fine. But if the user isn't already logged into Google, I get an error 'Uncaught SyntaxError: Unexepected token <'. I think this is the HTML that Google is sending back for their login page.
Is there a way I can get Angular to play nice with Node/Express/Passport here? Thanks in advance!
Set your CORS headers everytime while you're sending your response, even if you've handled them in your main file.
I'm running a node.js express 3 server with no proxies and using SSL.
I'm trying to figure out how to force all connections to go through https.
Google searching shows me this:
https://groups.google.com/forum/#!topic/express-js/Bm6yozgoDSY
There's currently no way to force https redirects, though that seems
like a bit of a strange work-around. We have an https-only app and we
just have a simple ~4 line node http server that redirects, nothing
fancy
Which is what I need, but he doesn't say what those 4 lines are.
How do we do this? Thanks.
I don't really understand the point in starting two servers when only one can do the job perfectly. For example, by adding a simple middleware in your server file:
app.use(function(req, res, next) {
if(!req.secure) {
return res.redirect(['https://', req.get('Host'), req.url].join(''));
}
next();
});
This will redirect any non-secure request to the corresponding HTTPS page. For example, http://example.com/ to https://example.com/ and http://example.com/foo?bar=woo to https://example.com/foo?bar=woo. This is definitely the behavior I would expect. Maybe you should filter this by host, so it redirects only on domains for which you own and installed a proper certificate.
If your app is running behind another server like Nginx, you may want to add the configuration parameter app.set('trust proxy', true). Or, even better, make Nginx do the redirect itself, which will be more efficient than any Node.js app.
Edit: According to my benchmarks, join is a little faster than + for concatenating strings. Nothing dramatic, but every win is a win...
You should create a second server listening on 80 and redirect with a 301 header to your https server:
var express = require('express');
var app = express();
app.get('/', function(req, res, next){
res.redirect('https://' + app.address().address)
});
app.listen(80);
I had a similar problem and the redirect solution is not suitable for me because essentially I want to get rid of the browser's insecure warning,
So instead of redirect every message, I did:
app1 = new express()
app1.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/redirect.html'));
});
app1.listen(80, function(){'redirect server running on 80 port'})
and in the redirect.html is just a redirecting html file:
<meta http-equiv="refresh" content="0; URL='https://my-site.com'" />
Of course, this won't work for complicated cases when you want to redirect all routings, but for me, I only want to redirect my homepage to my https homepage and get rid of the browser's insecure warning. Hope this help somebody!