I have an express app that uses Facebook authentication via Passport.js and the passport-facebook strategy. I have followed the documentation on how to handle authentication.
It all works fine... until Facebook tries to report an error via the callback Url. In this case, I get an uncaught FacebookAuthorizationError exception, and the failureRedirect is not called.
End users see an "Internal Server Error" as a result of the un-caught exception and I am in non-compliance with Facebook development guidelines...
http://localhost:8081/auth/facebook/callback?error_code=1340031&error_message=Unable+to+Log+In%3A+There+is+a+60-minute+delay+before+new+accounts+can+log+in+to+any+applications.+Please+try+again+in+an+hour.#_=_
FacebookAuthorizationError: Unable to Log In: There is a 60-minute delay before new accounts can log in to any applications. Please try again in an hour.
at Strategy.authenticate (/Users/Dan/project/node_modules/passport-facebook/lib/strategy.js:81:23)
at attempt (/Users/Dan/project/node_modules/passport/lib/middleware/authenticate.js:361:16)
at authenticate (/Users/Dan/project/node_modules/passport/lib/middleware/authenticate.js:362:7)
at Layer.handle [as handle_request] (/Users/Dan/project/node_modules/express/lib/router/layer.js:95:5)
I have read an older post (passport don't redirect to 'failureRedirect' upon failure with facebook) that it could be an open issue.
I would think this passport plugin would be extremely popular, and that someone would have a workaround - does anyone know?
Thanks a lot in advance.
function initExpress() {
const express = require('express'),
passport = require('passport'),
LocalStrategy = require('passport-local'),
FacebookStrategy = require('passport-facebook').Strategy,
expressSession = require('express-session');
}
let app = express();
app.use(expressSession(
{
secret: secret,
resave: false,
saveUninitialized: false
}
));
app.use(flash());
passport.use(new LocalStrategy(User.authenticate()));
passport.use(new FacebookStrategy({
clientID: process.env.FACEBOOK_APP_ID,
clientSecret: process.env.FACEBOOK_APP_SECRET,
callbackURL: `${process.env.SERVER_API_URL}/auth/facebook/callback`,
profileFields: ['id', 'displayName', 'name', 'picture']
},
function(accessToken, refreshToken, profile, done) {
facebookFindOrCreateUser(accessToken, refreshToken, profile, done);
}
));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
etc..
}
const express = require('express'),
router = express.Router(),
passport = require('passport');
router.get(
'/facebook',
passport.authenticate('facebook')
);
router.get('/facebook/callback',
passport.authenticate(
'facebook',
{
failureRedirect: '/login',
failureFlash: true
}
),
(req, res) => {
// Successful authentication
res.redirect('/authenticated');
}
);
module.exports = router;
Ok, I worked out a fix - via implementing a middleware error handler. Hope this is useful to someone - as I couldn't find any info about it in the passport-facebook documentation.
It catches the previously un-caught exception and prevents the "Internal Server Error" that Express was giving to end users.
const express = require('express'),
router = express.Router(),
passport = require('passport');
router.get(
'/facebook',
passport.authenticate('facebook')
);
function fbErrorHandler(err, req, res, next) {
// I use flash, but use whatever notification method you want for end users:
req.flash('error', 'Error while trying to login via Facebook: ' + err);
res.redirect('/login');
}
router.get('/facebook/callback',
passport.authenticate(
'facebook',
{
failureRedirect: '/login',
failureFlash: true
},
),
fbErrorHandler,
(req, res) => {
// Successful authentication
res.redirect('/authenticated');
}
);
module.exports = router;