14

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&notif_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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Eli White
  • 1,014
  • 1
  • 10
  • 20
  • 1
    passport oauth2 strategy uses req.connection.encrypted to check if it is in a secure connection. Could you try to see the output of it? Maybe you are actually running under an insecure connection. – Jonas Dec 29 '13 at 21:16
  • 2
    Jonas, That was a great comment, thanks for pointing me in the right direction. I found a post here: https://groups.google.com/forum/#!topic/express-js/Bm6yozgoDSY that comments that since SSL is handled by nginx on Heroku, req.connection.encrypted is always "undefined". Any ideas on how to work through that? – Eli White Dec 30 '13 at 19:26
  • Jonas, that comment led me to figuring out how to solve the problem (digging through passport code). If you can make an answer, I'll award it the reputation points. You are welcome to take my answer that I posted (in which case I will delete mine after). – Eli White Dec 30 '13 at 19:50
  • Great! I made an answer trying to put all the info together. – Jonas Dec 30 '13 at 22:04

5 Answers5

31

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.

Jonas
  • 1,692
  • 13
  • 17
9

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 }

Armando Musto
  • 101
  • 1
  • 3
2

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.

Eli White
  • 1,014
  • 1
  • 10
  • 20
0

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")

Ari Porad
  • 2,834
  • 3
  • 33
  • 51
  • 1
    Any ideas on how to dynamically generate the full url callback depending on what the request scheme / url was? That is one of the solutions I have thought about for a while, but I don't have access to req there, so I can't figure that stuff out. – Eli White Dec 29 '13 at 10:35
  • 1
    @EliWhite, No, I don't think you can. Also, See My Updated Answer – Ari Porad Dec 29 '13 at 19:55
0

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!

chemedev
  • 23
  • 6