Вход в Google с Passportjs не проходит проверку подлинности

Я использую паруса с паспортом для аутентификации. Я использую passport-google-oauth(OAuth2Strategy) и passport-facebook для включения входа в Google.

Я не слишком хорошо разбираюсь в Passport, так что извините, если это вопрос новичка. Я настроил вход через Facebook, и он работает просто отлично. В Google я получаю код авторизации после разрешения доступа к приложению, но в итоге я не прошел проверку подлинности. Я предполагаю, что один и тот же код должен работать как для Facebook, так и для Google, так как обе стратегии основаны на oauth2.

Я даже не уверен, каким кодом поделиться, так как я использую автоматически сгенерированный код из sails-generate-auth, но не знаю, могу ли я поделиться чем-то еще.

Любые идеи о том, почему это может происходить? Приложение размещается локально, но это вряд ли будет проблемой, так как я все равно добираюсь до стадии авторизации.

2 ответа

Решение

Хорошо, так что в конечном итоге это была известная проблема с API.

TL; DR: включить API Google+ и API контактов, как упомянуто здесь. (API контактов не требуется, как @AlexisN-o указал в комментариях. Моя установка работала как надо с отключенным API контактов. Это, очевидно, зависит от того, какую область вы используете.)

Я полагаю, что это не очень хороший способ потерпеть неудачу, так как это была ошибка API, которая была предотвращена. Во всяком случае, я копался в passport.authenticate выяснить, что пошло не так. Это в конечном итоге называет authenticate метод, определенный в пакете, соответствующем стратегии (в данном случае oauth2). Здесь (passport-google-oauth/lib/passport-google-oauth/oauth2.js) Я обнаружил, что accessToken действительно был извлечен из Google, так что все должно работать. Это указывало на проблему с запросами к URL-адресам токенов. Так что я рискнул немного дальше в passport-oauth2/lib/strategy.js и наконец удалось записать эту ошибку:

{ [InternalOAuthError: failed to fetch user profile]
  name: 'InternalOAuthError',
  message: 'failed to fetch user profile',
  oauthError: 
   { statusCode: 403,
     data: '{
        "error": {
            "errors": [{
                  "domain": "usageLimits",
                  "reason": "accessNotConfigured",
                  "message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration.",
                  "extendedHelp": "https://console.developers.google.com" 
            }],
            "code": 403,
            "message": "Access Not Configured. The API (Google+ API) is not enabled for your project. Please use the Google Developers Console to update your configuration." 
      }
    }'
} }

Это был конец охоты на меня, и первый результат поиска ошибок привел к правильному ответу. Странно исправить, хотя.

Я столкнулся с той же проблемой, и он был расположен здесь в api / services / passport.js:

// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
  user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
  user.username = profile.username;
}

// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
  return next(new Error('Neither a username nor email was available'));
}

Служба Google не возвращала profile.username собственность

Из-за этого пользователь не сохраняется в базе данных и не может быть аутентифицирован. Затем обратный вызов паспорта получает пустого пользователя, поэтому функция, обрабатывающая ошибки, запускается, и пользователь перенаправляется на страницу входа.

Это изменение позволяет использовать displayName собственность как username:

// If the profile object contains a list of emails, grab the first one and
// add it to the user.
if (profile.hasOwnProperty('emails')) {
  user.email = profile.emails[0].value;
}
// If the profile object contains a username, add it to the user.
if (profile.hasOwnProperty('username')) {
  user.username = profile.username;
}


/** Content not generated BEGIN */
// If the username property was empty and the profile object
// contains a property "displayName", add it to the user.
if (!user.username && profile.hasOwnProperty('displayName')) {
  console.log(profile);  // <= Use it to check the content given by Google about the user
  user.username = profile.displayName;
}
/** Content not generated END */


// If neither an email or a username was available in the profile, we don't
// have a way of identifying the user in the future. Throw an error and let
// whoever's next in the line take care of it.
if (!user.username && !user.email) {
  return next(new Error('Neither a username nor email was available'));
}

Вы также можете использовать profile.id собственность, потому что profile.displayName не обязательно уникален (то есть: две учетные записи Google могут иметь идентичные displayName). Но это также верно для разных служб: учетная запись Twitter также может иметь то же имя пользователя, что и учетная запись Facebook. Если оба зарегистрируются в вашем приложении, у вас будет ошибка. Это проблема из кода, сгенерированного sails-generate-auth и вы должны адаптировать его к поведению, которое вы хотите.

Я предложу PR, если это решение работает и для вас.

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