Паспорт FacebookTokenError из-за предварительной загрузки Chrome
Я работаю над веб-приложением, которое позволяет входить в систему через Facebook, используя Passport.js. Мой код выглядит следующим образом:
/* Passport.js */
var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
/* DB */
var User = require('../models/db').User;
exports.passport = passport;
passport.use(new FacebookStrategy(
{
clientID: '<ID>',
clientSecret: '<SECRET>',
callbackURL: 'http://localhost:4242/auth/facebook/callback'
},
function (accessToken, refreshToken, profile, done) {
console.log(profile.provider);
User.findOrCreate({ "provider": profile.provider,"id": profile.id },
function (err, user) { return done(err, user); });
}
));
passport.serializeUser(function(user, done) {
console.log('serialize');
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log('deserialize');
User.findOne({"id": id}, function(err, user) {
done(err, user);
});
});
Этот код отлично работает на Firefox; Мой пользователь проходит аутентификацию через Facebook, а затем успешно маршрутизирует. В Chrome, однако, я иногда получаю следующую ошибку:
FacebookTokenError: This authorization code has been used.
at Strategy.parseErrorResponse (/Users/Code/Web/node_modules/passport-facebook/lib/strategy.js:198:12)
at Strategy.OAuth2Strategy._createOAuthError (/Users/Code/Web/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:337:16)
at /Users/Code/Web/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:173:43
at /Users/Code/Web/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:162:18
at passBackControl (/Users/Code/Web/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:109:9)
at IncomingMessage.<anonymous> (/Users/Code/Web/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:128:7)
at IncomingMessage.EventEmitter.emit (events.js:117:20)
at _stream_readable.js:910:16
at process._tickCallback (node.js:415:13)
Мои печатные заявления показывают довольно неожиданное поведение, как показано на рисунках ниже:
Незавершенный URL, ожидающий отправки...
... приводит к выводу на печать в моем терминале.
Похоже, что Chrome пытается предварительно загрузить запрос в Facebook, вызывая состояние гонки, приводящее к ошибке, если клиент нажимает ввод в нужное время, как показано ниже:
Я подтвердил несколько запросов с Wireshark. Если я жду достаточно долго между автозаполнением и отправкой URL (скажем, 3 секунды), оба запроса завершаются без ошибок. Ошибка возникает, только если два запроса отправляются с интервалом чуть более секунды. Ошибка является уникальной для Chrome, так как Firefox отправляет только один запрос.
Я могу здесь что-нибудь сделать? Мое приложение, конечно, не может быть единственным, которое сталкивается с такой ошибкой, когда дело доходит до чего-то такого частого, как проверка подлинности Facebook. Можно ли как-то предотвратить загрузку Chrome? Если нет, то я смирился с тем, что поймал ошибку и просто попытался снова пройти аутентификацию?
Бонусный вопрос: я, кажется, десериализовался несколько раз для каждого запроса. Мой самый первый запрос напечатает следующее:
facebook
serialize
deserialize
Каждый последующий успешный запрос печатается
deserialize
deserialize
facebook
serialize
deserialize
при неудачном запросе пар печатают
deserialize
deserialize
deserialize
deserialize
/* Error */
facebook
serialize
Похоже, каждый запрос десериализуется дважды. Я прочитал этот отчет об ошибке, предлагая решение, но express.static
приходит раньше passport.session
в моем стеке промежуточного программного обеспечения, так что это не может быть моей проблемой.
Спасибо!
1 ответ
Я бы оставил это как комментарий, но у меня нет репутации. Но Chrome будет выполнять предварительную выборку страниц только тогда, когда вы что-то вводите в строку URL, но зачем вам или пользователю вводить вручную /auth/facebook
?
Одним из возможных решений было бы сделать /auth/facebook
Маршрут принимает только запросы POST. Это не позволит Chrome запускать маршрут при попытке предварительной загрузки.
Другое возможное решение, и я не уверен, насколько хорошо это будет работать, потребовало бы отметки времени в строке запроса, что-то вроде /auth/facebook?_t=1406759507255
, И только звонок passport.authenticate('facebook')
когда отметка времени достаточно близка к текущему времени. Но я не думаю, что любое из этих решений необходимо просто потому, что никто не должен вводить этот URL вообще.