У меня было много проблем, пытаясь заставить работать вход на стороне клиента, поэтому я позволю себе сослаться на массу других вопросов здесь... ни один из них не привел к ответу, который сработал для меня. .
КОНТЕКСТ
- Вход на стороне сервера работает нормально
- Вход на стороне клиента с использованием JS SDK отлично работает в Safari* (не тестировалось в Firefox, IE или мобильных устройствах, отличных от Chrome), но не в Chrome, о чем этот вопрос (и большинство мои пользователи используют Chrome, так что это очень важно)
- Gem versions:
- ruby (2.1.2)
- рельсы (4.1.1)
- аутентификация (0.4.7)
- оаут2 (1.0.0)
- омниаут (1.2.2)
- omniauth-facebook (2.0.0)
- omniauth-oauth2 (1.2.0)
- Это для приложения в режиме разработки, где я, как разработчик, определенно авторизован для входа в систему.
* Под "работает нормально", я имею в виду, если вы просто скопировали код из RailsCast Райана Бейтса (http://railscasts.com/episodes/360-facebook-authentication?view=asciicast) работает без каких-либо дополнительных действий
TL:DR; Я передаю следующий URL, который должен работать...
`http://localhost:3000/auth/facebook/callback?signed_request=ASDF.JOUYOUY`
// Obviously signed_request is a much longer string
... но я все еще получаю сообщение об ошибке OmniAuth::Strategies::Facebook::NoAuthorizationCodeError (must pass either a
codeparameter or a signed request (via
signed_requestparameter or a
fbsr_XXXcookie)):
УСТРАНЕНИЕ НЕПОЛАДОК НА ДАННЫЙ МОМЕНТ + ОБЗОР МНОГИХ ОШИБОК/ ВОЗМОЖНЫХ РЕШЕНИЙ
Приведенные ниже кодовые решения основаны на коде из RailsCast Райана Бейтса:
$('#sign_in').click (e) ->
e.preventDefault()
FB.login (response) ->
window.location = '/auth/facebook/callback' if response.authResponse
Препятствие 1. Блокируются ли сторонние файлы cookie?
Не обязательно приводит к ошибке, просто делает так, что вы не можете подключить приложение к Facebook.
Для подключения вашего приложения к Facebook сначала необходимо войти в систему Facebook, потому что код FB.login (response) -> window.location = "/auth/facebook/callback" if response.authResponse
зависит от правильности authResponse
и содержащегося в нем параметра signedRequest
.
Если файлы cookie заблокированы (например, Настройки Chrome > Дополнительные настройки > Конфиденциальность > Настройки содержимого > Блокировать сторонние файлы cookie отмечены ПРОВЕРКОЙ), вы никогда не получите объект authResponse
обратно. Это комбинация вопроса и ответа здесь: /а>. Другими словами, если вы делаете FB.getLoginStatus
, независимо от того, сколько раз вы нажимаете кнопку входа, статус всегда будет возвращаться как unknown
согласно документам: https://developers.facebook.com/docs/ссылка/javascript/FB.getLoginStatus.
Код Решение на данный момент...: если FB.login
возвращает ответ unknown
, перенаправить обратно на вход на стороне сервера.
$('#sign_in').click (e) ->
e.preventDefault()
FB.login (response) ->
if response.status is "unknown"
window.location = "/auth/facebook"
else
window.location = "/auth/facebook/callback"
Препятствие 2. Правильно ли передаются параметры?
Если у вас разблокированы сторонние файлы cookie, вы можете столкнуться с ошибкой:
OmniAuth::Strategies::Facebook::NoAuthorizationCodeError (must pass either a `code` parameter or a signed request (via `signed_request` parameter or a `fbsr_XXX` cookie)):
Несмотря на установку cookie: true
в FB.init
, иногда параметры все равно не передаются. Как указано в:
- Этот ответ SO (OmniAuth Strategies Facebook NoAuthorizationCodeError (должен пройти либо ` code` или подписанный запрос (через параметр signed_request):
- Автор Omniauth-facebook в последнем комментарии к этой проблеме Github (https://github.com/mkdynamic/omniauth-facebook/issues/110)
... вам может потребоваться вручную передать параметр signed_request
. Быстрое наблюдение. Согласно документам Facebook (https://developers.facebook.com/docs/facebook-login/using-login-with-games), signedRequest
должен состоять из двух компонентов, разделенных .
. Не знаю как, но я уже получал один без .
. Разделение важно, потому что вторая половина представляет собой закодированный base64url
объект JSON, содержащий информацию code
.
Код решения на данный момент...: вручную добавить параметр signed_request
к URL-адресу обратного вызова.
$('#sign_in').click (e) ->
e.preventDefault()
FB.login (response) ->
if response.status is "unknown"
window.location = "/auth/facebook"
else
window.location = ("/auth/facebook/callback?signed_request=" + response.authResponse.signedRequest)
Препятствие №3: по-прежнему ничего... все та же ошибка, что и №2
Может только я на данный момент?
Решение кода на данный момент...: проявите суперпедантичность, отделите code
от signedRequest
и добавьте к URL-адресу обратного вызова.
$('#sign_in').click (e) ->
e.preventDefault()
FB.login (response) ->
if response.status is "unknown"
window.location = "/auth/facebook"
else
parsed_signed_Request = response.authResponse.signedRequest.split(".")
key_info = base64_decode(parsed_signed_Request[1])
// decoded via http://www.simplycalc.com/base64-source.php
key_info_object = jQuery.parseJSON( key_info )
window.location = ("/auth/facebook/callback?code=" + key_info_object["code"])
Препятствие № 4: CSRF обнаружен, но не по той причине, о которой вы подумали
Теперь я переехал прямо в кирпичную стену. Когда я запускаю код, как указано выше, я получаю обнаруженную ошибку CSRF. Есть несвязанная причина, по которой можно получить эту ошибку:
- Вы можете получить эту ошибку, если ваше приложение Facebook находится в режиме разработки и вы пытаетесь войти в систему в режиме реального времени. В этом случае FB не позволяет разработчикам, не включенным в список, входить в систему. См. первый ответ на этот вопрос: Rails + omniauth + facebook - обнаружен csrf
Но проблема в моем случае была не в том, что выше, а в том, что параметр code
был представлен без параметра state
. Теперь были ответы, в которых говорилось, что вы можете исправить это, установив provider_ignores_state: true
в файле конфигурации omniauth.rb
, см. второй ответ на вопрос, указанный выше, НО это не исправление для меня. Почему? Потому что 1) мне не нравится отключать вещи, специально предназначенные для противодействия CSRF, и 2) кажется, что конфигурация срабатывает только для входа на стороне сервера. Добавление этого просто ничего не сделало для меня.
Это означает, что более серьезная проблема решения № 3 заключалась в том, что оно пыталось объединить подход к входу на стороне сервера (принимает параметры code
и state
) с подходом входа на стороне клиента (принимает параметр signed_request
).
Это означает, что я вернулся к исходному вопросу... как передать signed_request
, чтобы вход на стороне клиента работал?
Поскольку я уже много болтал об этом, позвольте мне указать на другую ошибку, которую я видел. У этого есть ответы, связанные с ошибками Facebook (Работа с ошибкой Oauth 2.0-facebook gem 100: этот код авторизации был использован), но помимо этого я нашел еще кое-что, что могло ее вызвать.
Как предлагается в этом руководстве (https://coderwall.com/p/bsfitw), вы match
маршрут обратного вызова через get
и post
. Но когда я это делаю, мой logger
показывает два запроса к Facebook, второй явно заблокирован и вызывает ошибку. (Также означает, что первый запрос прошел и пользователь уже авторизован/данные сохранены, что угодно). Мое решение состояло в том, чтобы установить маршрут так:
match 'auth/:provider/callback', to: 'signups#create_facebook', via: [:get]