Я реализую Sign In с Facebook в своем проекте, который состоит из двух приложений:
- Внешний интерфейс: обычный HTML+CSS+JS;
- Бэкенд: Rails 5 API (omniauth 1.3.1, omniauth-facebook 3.0.0);
Даже в моей локальной среде оба приложения работают в разных доменах (frontend.dev.azk.io
и api.dev.azk.io
).
Я запускаю FB SDK в своем внешнем приложении, и после успешного входа в систему я должен пропинговать обратный вызов OAuth (http://api.dev.azk.io/api/v1/users/auth/facebook/callback
), чтобы omniauth-facebook
мог получить доступ к файлу cookie fbsr_XXX
, проанализировать его и обменять текущий accessToken
на долгосрочный.
Основная проблема в том, что кука, установленная FB SDK, принадлежит фронтенд-домену и не отправляется в заголовке запроса к API, т.е. omniauth-facebook
не может получить к нему доступ:
ERROR -- omniauth: (facebook) Authentication failure! no_authorization_code: OmniAuth::Strategies::Facebook::NoAuthorizationCodeError, must pass either a `code` (via URL or by an `fbsr_XXX` signed request cookie)
В этот момент я подумал о двух возможных подходах:
1) Проверить конфиги CORS и AJAX, чтобы куки можно было отправлять в запросе к API;
2) Проанализируйте Ответ FB во внешнем интерфейсе (используя этот алгоритм) и отправьте атрибут code
к API;
Подход 1
Вдохновленный этим невероятно похожим вопросом здесь, в Итак, я сделал следующие конфигурации:
routes.rb
devise_for :users, path_prefix: 'api/v1', controllers: {
sessions: 'api/v1/sessions',
registrations: 'api/v1/registrations',
omniauth_callbacks: 'api/v1/omniauth_callbacks',
}
application.rb
config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'frontend.dev.azk.io'
resource '*',
headers: :any,
credentials: true,
methods: [:get, :put, :post, :patch, :delete, :options]
end
end
devise.rb
config.omniauth :facebook, oauth_providers['facebook']['app_id'], oauth_providers['facebook']['secret'], {
scope: 'email,user_birthday,user_about_me,public_profile',
provider_ignores_state: true
}
Клиентское приложение
$.ajax({
type: 'GET',
url: 'http://api.dev.azk.io/api/v1/users/auth/facebook/callback',
crossDomain: true,
xhrFields: { withCredentials: true },
}) . . .
Однако этого недостаточно для отправки файла cookie fbsr_XXX
(присутствует в браузере). Есть ли что-то, что я упускаю?
Подход 2
Поскольку omniauth-facebook
не может получить доступ к вышеупомянутому файлу cookie, я решил проанализировать значение signedRequest
, возвращаемое FB, и отправить code
атрибут для API.
Однако кажется, что omniauth-facebook
генерирует callback_url
, который не соответствует тому, который использовался в предыдущем запросе:
ERROR -- omniauth: (facebook) Authentication failure! invalid_credentials: OAuth2::Error, :
{"error":{"message":"Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request","type":"OAuthException","code":100,"fbtrace_id":"BavpcDFblHy"}}
Мне действительно не нравится второй подход, я думаю, что первый намного элегантнее. Я думаю, что этот процесс должен быть простым, возможно, я упускаю что-то глупое.