I'm implementing Sign In with Facebook on my project, which consists in two applications:
- Frontend: plain HTML+CSS+JS;
- Backend: Rails 5 API (omniauth 1.3.1, omniauth-facebook 3.0.0);
Even in my local environment, both applications run on different domains (frontend.dev.azk.io
and api.dev.azk.io
).
I run the FB SDK in my frontend app, and once the login succeeds, I should ping the OAuth callback (http://api.dev.azk.io/api/v1/users/auth/facebook/callback
) so that omniauth-facebook
could access the fbsr_XXX
cookie, parse it and exchange the current accessToken
for a long term one.
The main issue is that the cookie set by FB SDK belongs to the frontend domain and isn't sent in the header of the request to the API, i.e. omniauth-facebook
cannot access it:
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)
At this point, I thought in two possible approaches:
1) Check CORS and AJAX configs so that the cookie could be sent in the request to the API;
2) Parse the FB response in the frontend (using this algorithm) and send the code
attribute to the API;
Approach 1
Inspired by this incredibly similar question here in SO, I've made the following configurations:
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
}
Client Application
$.ajax({
type: 'GET',
url: 'http://api.dev.azk.io/api/v1/users/auth/facebook/callback',
crossDomain: true,
xhrFields: { withCredentials: true },
}) . . .
However, this isn't enough for sending the fbsr_XXX
cookie (present in the browser). Is there anything I'm missing?
Approach 2
Since omniauth-facebook
can't access the aforementioned cookie, I've decided to parse the signedRequest
value returned by FB and send the code
attribute for the API.
However, seems that omniauth-facebook
generates the callback_url
, which mismatch with the one used in the previous request:
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"}}
I really don't like the 2nd approach, I think the first one is way more elegant. I think this process should be simple, probably I'm missing something silly.