我在我的项目中使用Facebook实施登录,该项目包含两个应用程序:
即使在我的本地环境中,这两个应用程序都在不同的域上运行(frontend.dev.azk.io
和api.dev.azk.io
)。
我在我的前端应用程序中运行FB SDK,一旦登录成功,我应该ping OAuth回调(http://api.dev.azk.io/api/v1/users/auth/facebook/callback
),以便omniauth-facebook
可以访问fbsr_XXX
cookie,解析它并将当前的accessToken
换成长期的。{/ p>
主要问题是FB SDK设置的cookie属于前端域,并且未在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配置,以便cookie可以在请求中发送给API;
2)Parse前端的FB响应(使用this algorithm)并将code
attribute发送给API;
受SO incredibly similar question的启发,我做了以下配置:
的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 },
}) . . .
然而,这还不足以发送fbsr_XXX
cookie(存在于浏览器中)。我有什么遗失的吗?
由于omniauth-facebook
无法访问上述Cookie,因此我决定解析FB返回的signedRequest
值并发送code
attribute以获取API。< / p>
但是,似乎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"}}
不匹配:
proc sql;
create table ca_m_service as
select a.month_key, a.service_key, a.tot_revenue, a.TOT_REVENUE_DISCNT, a.euser_key, b.*
from fr_dme.M_service_rev_by_euser_f as a
inner join code_remise_ass as b
on a.service_key = b.service_key
inner join code_remise_ass as c
on a.service_key = c.parent_service_key
where a.month_key between 201504 and 201509;
quit;
我真的不喜欢第二种方法,我认为第一种方式更优雅。我认为这个过程应该很简单,可能我错过了一些愚蠢的东西。