Ruby-on-rails OmniAuth 和 Facebook:证书验证失败
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3977303/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
OmniAuth & Facebook: certificate verify failed
提问by shmichael
I've followed Railscast #235to try and set up a minimal Facebook authentication.
我已经按照Railscast #235尝试设置最小的 Facebook 身份验证。
I've first set up a Twitter authentication, as done by Ryan himself. That worked flawlessly.
我首先设置了 Twitter 身份验证,正如 Ryan 本人所做的那样。那工作完美无缺。
I then moved on to adding a Facebook login. However, after authorizing the app the redirect to /auth/facebook/callbackfails with:
然后我继续添加 Facebook 登录。但是,在授权应用程序后,重定向/auth/facebook/callback失败并显示:
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
I am working on localhost. I didn't set up any SSL within the app. What am I doing wrong?
我正在本地主机上工作。我没有在应用程序中设置任何 SSL。我究竟做错了什么?
回答by Alex Kremer
The real problem is that Faraday (which Omniauth/Oauth use for their HTTP calls) is notwasn't setting the ca_path variable for OpenSSL. At least on Ubuntu, most root certs are stored in "/etc/ssl/certs". Since Faraday isn'twasn't setting this variable (and currently does not have a method to do so), OpenSSL isn'twasn't finding the root certificate for Facebook's SSL certificate.
真正的问题是 Faraday(Omniauth/Oauth 用于他们的 HTTP 调用)没有为 OpenSSL 设置 ca_path 变量。至少在 Ubuntu 上,大多数根证书都存储在“/etc/ssl/certs”中。由于法拉第是不是没有设置这个变量(和目前没有这样做的方法),OpenSSL的不被发现不为Facebook的SSL证书的根证书。
I've submitted a pull request to Faradaywhich will add support for this variable and hopefully they will pull in this change soon. Until then, you can monkeypatch faraday to look like thisor use my fork of Faraday. After that, you should specify version 0.3.0 of the OAuth2 gem in your Gemspec which supports the passing of SSL options through to Faraday.All you need to do now is upgrade to Faraday 0.6.1, which supports passing of the ca_path variable and upgrade to OmniAuth 0.2.2, which has the proper dependencies for OAuth2. You'll then be able to properly fix this issue by just adding the following to your Omniauth initializer:
我已经向 Faraday 提交了一个拉取请求,它将添加对这个变量的支持,希望他们能尽快完成这个更改。在此之前,您可以使用猴子补丁法拉第看起来像这样或使用我的法拉第叉。之后,您应该在 Gemspec 中指定 OAuth2 gem 的 0.3.0 版本,它支持将 SSL 选项传递给 Faraday。您现在需要做的就是升级到 Faraday 0.6.1,它支持传递 ca_path 变量并升级到 OmniAuth 0.2.2,它具有 OAuth2 的适当依赖项。然后,您只需将以下内容添加到您的 Omniauth 初始值设定项即可正确解决此问题:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, FACEBOOK_KEY, FACEBOOK_SECRET, {:client_options => {:ssl => {:ca_path => "/etc/ssl/certs"}}}
end
So, to recap:
所以,回顾一下:
Faraday needs to be updated to support SSL ca_path.Install Faraday 0.6.1Your app needs to use OAuth2 version 0.3.0. You may need to fork omniauth since it currently has a minor version dependency in the 0.2.x tree.Upgrade to OmniAuth 0.2.2- Modify your provider initializer to point to your system's certificate path ("/etc/ssl/certs" on Ubuntu et al)
Faraday 需要更新以支持 SSL ca_path。安装法拉第 0.6.1您的应用需要使用 OAuth2 版本 0.3.0。您可能需要 fork omniauth,因为它目前在 0.2.x 树中有一个次要版本依赖项。升级到 OmniAuth 0.2.2- 修改您的提供者初始值设定项以指向您系统的证书路径(Ubuntu 等上的“/etc/ssl/certs”)
Hopefully the next releases of both Faraday and Omniauth will incorporate this solution.
希望 Faraday 和 Omniauth 的下一个版本将包含此解决方案。
Thanks to KirylP above for setting me on the right path.
感谢上面的 KirylP 让我走上了正确的道路。
回答by Emerson
I was having this problem and tried using the :ca_path argument without success. After looking through Github for awhile, I came across a suggestion that mentioned using :ca_file and point directly to the certification.
我遇到了这个问题并尝试使用 :ca_path 参数但没有成功。在浏览了 Github 一段时间后,我遇到了一个提到使用 :ca_file 并直接指向认证的建议。
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook, 'secret_key', 'secret_key',
:client_options => {:ssl => {:ca_file => '/etc/pki/tls/certs/ca-bundle.crt'}}}
end
If you need to get the path to your systems certification files (and your using linux) simply type from the terminal. This will give you a bunch of information about your SSL setup, including the path (refer to OPENSSLDIR). You'll need to add certs/ca-bundle.crt to the path provided.
如果您需要获取系统认证文件(以及您使用的 linux)的路径,只需从终端输入即可。这将为您提供一系列有关 SSL 设置的信息,包括路径(请参阅 OPENSSLDIR)。您需要将 certs/ca-bundle.crt 添加到提供的路径中。
open-ssl version -a
回答by Simple Dimple
I am on ubuntu 10.10 (Maverick)... struggled about 6 hours before I got it to work, sharing my experience
我在 ubuntu 10.10(Maverick)上......在我开始工作前挣扎了大约 6 个小时,分享我的经验
- did not try monkey patch
- tried {:client_options => {:ssl => {:ca_path => "/etc/ssl/certs"}} but still not worked
- tried ruby 1.8.7 still not worked
- tried different versions of omniauth & faraday, still no luck.
- 没有尝试猴子补丁
- 试过 {:client_options => {:ssl => {:ca_path => "/etc/ssl/certs"}} 但仍然没有工作
- 试过 ruby 1.8.7 还是不行
- 尝试了不同版本的 omniauth 和 faraday,仍然没有运气。
The only thing that made it to work was following (thanks Alex)
唯一使它起作用的是跟随(感谢亚历克斯)
if Rails.env.development?
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
end
回答by KirylP
Managed to go through SSL Certificate Verification like it has to be. My project is using 37signals ID for Basecamp integration (Ruby 1.9.2-p130, Rails 3.0.4).
设法通过 SSL 证书验证,就像它必须的那样。我的项目使用 37signals ID 进行 Basecamp 集成(Ruby 1.9.2-p130,Rails 3.0.4)。
RAILS_ROOT/config/initializers/omniauth.rb:
RAILS_ROOT/config/initializers/omniauth.rb:
require 'omniauth/oauth'
Rails.application.config.middleware.use OmniAuth::Strategies::ThirtySevenSignals,
'CLIENT_ID', 'CLIENT_SECRET', {client_options: {ssl: {ca_file: Rails.root.join('gd_bundle.crt').to_s}}}
module OAuth2
class Client
def initialize(client_id, client_secret, opts = {})
adapter = opts.delete(:adapter)
self.id = client_id
self.secret = client_secret
self.site = opts.delete(:site) if opts[:site]
self.options = opts
self.connection = Faraday::Connection.new(site, {ssl: opts.delete(:ssl)})
self.json = opts.delete(:parse_json) # ^ my code starts here
if adapter && adapter != :test
connection.build { |b| b.adapter(adapter) }
end
end
end
end
Where 'CLIENT_ID', 'CLIENT_SECRET' you can get at 37signals.comand certificates bundle file gd_bundle.crtfrom GoDaddybecause 37signals are using their CA.
其中“CLIENT_ID”、“CLIENT_SECRET”您可以在37signals.com和来自GoDaddy 的证书捆绑文件gd_bundle.crt获得,因为 37signals 正在使用他们的 CA。
回答by Anand Iyer
If you are deploying to Heroku, you want to point to the specific file location. This works for me (in config/initializers/omniauth.rb):
如果您要部署到 Heroku,您需要指向特定的文件位置。这对我有用(在 config/initializers/omniauth.rb 中):
Rails.application.config.middleware.use OmniAuth::Builder do
# This cert location is only for Heroku
provider :facebook, APP_ID, APP_SECRET, {:client_options => {:ssl => {:ca_file => "/usr/lib/ssl/certs/ca-certificates.crt"}}}
end
回答by Palo Delin?ák
I solved this with CA bundle from: http://certifie.com/ca-bundle/
我用 CA 包解决了这个问题:http: //certifie.com/ca-bundle/
And in my Devise initializer:
在我的设计初始值设定项中:
:client_options => { :ssl => { :ca_file => "#{Rails.root}/config/ca-bundle.crt" } } }
回答by antpaw
all of the solutions didnt work for me, then i've found this
所有的解决方案都不适合我,然后我找到了这个
http://railsapps.github.io/openssl-certificate-verify-failed.html
http://railsapps.github.io/openssl-certificate-verify-failed.html
rvm osx-ssl-certs update all
osx 10.8 ruby 2.0.0 via rvm
osx 10.8 ruby 2.0.0 通过 rvm
回答by Zac Randles
Looks like Omniauth now uses a newer version of Faraday, which explains why the monkey patch above wasn't working for me. I agree there must be a better way, but for anyone else who just needs to get it working to test, here's an updated version:
看起来 Omniauth 现在使用较新版本的法拉第,这解释了为什么上面的猴子补丁对我不起作用。我同意一定有更好的方法,但对于只需要让它工作进行测试的其他人,这里有一个更新的版本:
(create a file in your initializers directory with the following code)
(使用以下代码在您的初始化程序目录中创建一个文件)
require 'faraday'
module Faraday
class Adapter
class NetHttp < Faraday::Adapter
def call(env)
super
url = env[:url]
req = env[:request]
http = net_http_class(env).new(url.host, url.inferred_port)
if http.use_ssl = (url.scheme == 'https' && env[:ssl])
ssl = env[:ssl]
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.cert = ssl[:client_cert] if ssl[:client_cert]
http.key = ssl[:client_key] if ssl[:client_key]
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
http.cert_store = ssl[:cert_store] if ssl[:cert_store]
end
http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
http.open_timeout = req[:open_timeout] if req[:open_timeout]
if :get != env[:method]
http_request = Net::HTTPGenericRequest.new \
env[:method].to_s.upcase, # request method
!!env[:body], # is there data
true, # does net/http love you, true or false?
url.request_uri, # request uri path
env[:request_headers] # request headers
if env[:body].respond_to?(:read)
http_request.body_stream = env[:body]
env[:body] = nil
end
end
begin
http_response = if :get == env[:method]
# prefer `get` to `request` because the former handles gzip (ruby 1.9)
http.get url.request_uri, env[:request_headers]
else
http.request http_request, env[:body]
end
rescue Errno::ECONNREFUSED
raise Error::ConnectionFailed, $!
end
http_response.each_header do |key, value|
response_headers(env)[key] = value
end
env.update :status => http_response.code.to_i, :body => http_response.body
@app.call env
end
end
end
end
回答by Warren Wilkinson
I'm using Faraday 0.6.1, and OAUTH2 (alone, not wrapped by anything). This was enough to solve the problem for me (on Gentoo, should work on Ubunto)
我正在使用法拉第 0.6.1 和 OAUTH2(单独使用,没有被任何东西包裹)。这足以解决我的问题(在 Gentoo 上,应该在 Ubunto 上工作)
Turn this
转这个
client = OAuth2::Client.new(FACEBOOK_API_KEY, FACEBOOK_API_SECRET, :site => FACEBOOK_API_SITE)
Into this
成这个
client = OAuth2::Client.new(FACEBOOK_API_KEY, FACEBOOK_API_SECRET, :site => FACEBOOK_API_SITE, :ssl => {:ca_path => '/etc/ssl/certs' })
回答by Hyman Kinsella
Edit: Check the answer below as it is more relevant
编辑:检查下面的答案,因为它更相关
This worked for me (fix courtesy of https://github.com/jspooner):
这对我有用(修复由https://github.com/jspooner 提供):
Create a file in your initializer's directory with the following monkey patch:
使用以下猴子补丁在初始化程序的目录中创建一个文件:
require 'faraday'
module Faraday
class Adapter
class NetHttp < Faraday::Adapter
def call(env)
super
is_ssl = env[:url].scheme == 'https'
http = net_http_class(env).new(env[:url].host, env[:url].port || (is_ssl ? 443 : 80))
if http.use_ssl = is_ssl
ssl = env[:ssl]
if ssl[:verify] == false
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
else
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # <= PATCH or HACK ssl[:verify]
end
http.cert = ssl[:client_cert] if ssl[:client_cert]
http.key = ssl[:client_key] if ssl[:client_key]
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
end
req = env[:request]
http.read_timeout = net.open_timeout = req[:timeout] if req[:timeout]
http.open_timeout = req[:open_timeout] if req[:open_timeout]
full_path = full_path_for(env[:url].path, env[:url].query, env[:url].fragment)
http_req = Net::HTTPGenericRequest.new(
env[:method].to_s.upcase, # request method
(env[:body] ? true : false), # is there data
true, # does net/http love you, true or false?
full_path, # request uri path
env[:request_headers]) # request headers
if env[:body].respond_to?(:read)
http_req.body_stream = env[:body]
env[:body] = nil
end
http_resp = http.request http_req, env[:body]
resp_headers = {}
http_resp.each_header do |key, value|
resp_headers[key] = value
end
env.update \
:status => http_resp.code.to_i,
:response_headers => resp_headers,
:body => http_resp.body
@app.call env
rescue Errno::ECONNREFUSED
raise Error::ConnectionFailed.new(Errno::ECONNREFUSED)
end
def net_http_class(env)
if proxy = env[:request][:proxy]
Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
else
Net::HTTP
end
end
end
end
end

