Паспорт 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, ожидающий отправки...

Незавершенный 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 вообще.

Другие вопросы по тегам