OmniAuth & Facebook: certificate verify failed [duplicate]
Asked Answered
P

18

66

I've followed Railscast #235 to try and set up a minimal Facebook authentication.

I've first set up a Twitter authentication, as done by Ryan himself. That worked flawlessly.

I then moved on to adding a Facebook login. However, after authorizing the app the redirect to /auth/facebook/callback fails with:

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?

Paraffin answered 20/10, 2010 at 11:33 Comment(3)
Is there any hope of this 1.9.2 issue being "officially" resolved? I might not properly understand the answer below, but the if statement with "PATCH or HACK" seems useless (why have the if statement at all?), and there's no explanation about what the overall code's purpose is, aside from preventing the error.Hausfrau
@closurecowboy if you look at Alex Kremer's answer, he thoroughly covers why this error is occurring. You actually want to avoid the solution marked as the answer in your production code, as using VERIFY_NONE disables SSL certificate peer checking. I've adapted his solution into a wiki page for Omniauth. If you or anyone else has anything to add to it, please edit it to improve upon it.Maturate
General solution for all Rails apps here https://mcmap.net/q/102208/-openssl-ssl-sslerror-on-heroku-duplicateAlderney
J
74

The real problem is that Faraday (which Omniauth/Oauth use for their HTTP calls) is not wasn't setting the ca_path variable for OpenSSL. At least on Ubuntu, most root certs are stored in "/etc/ssl/certs". Since Faraday isn't wasn't setting this variable (and currently does not have a method to do so), OpenSSL isn't wasn't finding the root certificate for Facebook's SSL certificate.

I've submitted a pull request to Faraday which will add support for this variable and hopefully they will pull in this change soon. Until then, you can monkeypatch faraday to look like this or 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:

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:

  1. Faraday needs to be updated to support SSL ca_path. Install Faraday 0.6.1
  2. Your 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
  3. Modify your provider initializer to point to your system's certificate path ("/etc/ssl/certs" on Ubuntu et al)

Hopefully the next releases of both Faraday and Omniauth will incorporate this solution.

Thanks to KirylP above for setting me on the right path.

Juanjuana answered 11/4, 2011 at 7:23 Comment(7)
works on heroku too!Indiction
Works like a charm. r1.8.7, omniauth0.2.2 Thanks a lotAlliteration
How would you fix this error in a development environment where I do not have SSL certificates on my machine?Effendi
@Effendi The simplest would probably be to download and install them -- if you're on a Mac, you likely already have them if you've installed OpenSSL. Otherwise, if it's really just for development, you could add an initializer that turns off peer verification: if Rails.env.development? OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE endJuanjuana
+1 @Alex Kremer I used this: Rails.application.config.middleware.use OmniAuth::Builder do :facebook, FACEBOOK_KEY, FACEBOOK_SECRET, {:client_options => {:ssl => {:ca_path => "/etc/ssl/certs"}}, :scope => 'publish_stream,email'} end and it works, Thanks!Ludwigshafen
BTW, does anyone know how exactly do you specify the "cacerts.pem" path in Windows? Tried both of these: "C:\\cacerts.pem" and "C:/cacerts.pem" (yes, the file is there) without luck. Still SSL error. Anyone?Dasie
Take a look at my answer hete https://mcmap.net/q/102208/-openssl-ssl-sslerror-on-heroku-duplicateAlderney
T
18

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.

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.

open-ssl version -a
Thirddegree answered 30/4, 2011 at 6:1 Comment(2)
A thousand thanks to you Emerson! I had this problem on Cent OS. Some keywords for Google: ca_file, Cent OS, Redhat, Fedora, pki, TLS, SSL. thxIre
This worked for me, where ca_path didn't, on Amazon Linux.Omalley
S
14

I am on ubuntu 10.10 (Maverick)... struggled about 6 hours before I got it to work, sharing my experience

  1. did not try monkey patch
  2. tried {:client_options => {:ssl => {:ca_path => "/etc/ssl/certs"}} but still not worked
  3. tried ruby 1.8.7 still not worked
  4. tried different versions of omniauth & faraday, still no luck.

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
Stannite answered 23/4, 2011 at 17:41 Comment(3)
Worked on OS X as well for me! Thanks! As the verification happens only in development environment this shouldn't be a security risk in productionInstate
same thing! I am going with this solution cuz none of existing ones works for me =(Starbuck
Bad idea to disable SSL verification! Don't do it!Culottes
G
7

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).

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.com and certificates bundle file gd_bundle.crt from GoDaddy because 37signals are using their CA.

Gard answered 11/2, 2011 at 15:2 Comment(0)
B
6

If you are deploying to Heroku, you want to point to the specific file location. This works for me (in 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
Bautzen answered 24/5, 2011 at 15:0 Comment(1)
So how did you find/figure that out?Cord
Z
5

I solved this with CA bundle from: http://certifie.com/ca-bundle/

And in my Devise initializer:

:client_options => { :ssl => { :ca_file => "#{Rails.root}/config/ca-bundle.crt" } } }
Zins answered 28/6, 2011 at 12:49 Comment(0)
A
4

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:

(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
Alisaalisan answered 7/4, 2011 at 17:40 Comment(6)
I also agree there has to be a better way, but your post helped me out tons. My code was previously working with Omniauth 0.2.0 (which used Faraday 0.5.7 instead of 0.6.0), so I just put gem 'omniauth 0.2.0' in my gemfile and that'll allow me to finish developing some features first.Maturate
I noticed that this is not working for Faraday 0.6.1 can you update the code please ?Dews
Randles It is not working, giving error- No Method error: undefined method response_headers' for #<Faraday::Adapter::NetHttp:0xb6d0e6dc>` can you suggest any supplement of this, or any modification in needed in this to make it work?Ludwigshafen
Looks like this patch is out of date. I think you should look at Alex's updated answer for the solution.Alisaalisan
I'm sorry but no. Any answer that involves OpenSSL::SSL::VERIFY_NONE is going to be extremely insecure and should not be used, even if it "works".Toshikotoss
Just like @BobAman said, you should not even consider that an option!Ambagious
L
4

all of the solutions didnt work for me, then i've found this

http://railsapps.github.io/openssl-certificate-verify-failed.html

rvm osx-ssl-certs update all

osx 10.8 ruby 2.0.0 via rvm

Lordan answered 13/8, 2013 at 18:38 Comment(2)
I found this solution myself and I was going to end of answers to write it. +1 from me.Baedeker
This worked for me and it's simple.Brattice
A
2

Edit: Check the answer below as it is more relevant

This worked for me (fix courtesy of 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
Audrieaudris answered 7/12, 2010 at 17:7 Comment(2)
Not relevant for the new version of Faraday. check the answer bellow ...Dews
Disabling SSL verification is a bad way to solve this problem. Please point OpenSSL to the correct certificates instead.Stickweed
R
2

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)

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' })
Roose answered 8/5, 2011 at 2:29 Comment(1)
This helped me. For Oauth2 0.5.1 it is like this: oauth_client = OAuth2::Client.new( fb_app_id, fb_app_secret, { :site => 'graph.facebook.com', :token_url => '/oauth/access_token', :connection_opts => {:ssl => {:ca_path => "/etc/ssl/certs"}} })Fundamentalism
H
1

My problem was solved by ensuring that openSSL was using the right certificate directory:

For my system(ubuntu64) this was: ENV['SSL_CERT_DIR'] = '/usr/share/ca-certificates/'

This was using jruby-openssl with JRuby 1.6.0

I just added this setting to development.rb

Hydrometer answered 24/4, 2011 at 1:27 Comment(0)
R
1

I know this sounds trivial, but make sure you are using the right protocol. I kept getting this error and then realized that I was trying to connect via http. 1.5 hours wasted because I am an idiot.

Rosalvarosalyn answered 4/12, 2012 at 11:33 Comment(0)
P
0

This seems to be a 1.9.x issue. Reverting to 1.8.7 fixed the issue.

Paraffin answered 20/10, 2010 at 14:51 Comment(2)
But still worth finding a Ruby 1.9.2 fix....Audrieaudris
I have that error with Ruby 1.8.7 too and jRuby -- It is an installation and configuration problem. Check the other links above.Rodrigo
E
0

Here's what I did that helped if you are specifically having a problem on Leopard.

My cert was old and needed to be updated. I downloaded this:

http://curl.haxx.se/ca/cacert.pem

Then replaced my cert which was found here on Leopard:

/usr/share/curl/curl-ca-bundle.crt

Reload whatever you have that's accessing it and you should be good to go!

Egypt answered 25/8, 2011 at 4:6 Comment(0)
M
0

Just because instructions were a slight bit different for what worked for me, I thought I add my 2 cents:

I'm on OS X Lion and using macports and rvm

I installed curl-ca-bundle:

sudo port install curl-ca-bundle

Then I adjusted my omniauth config to be this:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :google_oauth2, APP_CONFIG['CONSUMER_KEY'], APP_CONFIG['CONSUMER_SECRET'],
           :scope => 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo.profile',
           :ssl => {:ca_path => "/share/curl/curl-ca-bundle.crt"}
end
Madaras answered 24/3, 2012 at 16:58 Comment(0)
E
0

On Ubuntu, all I had to do was update /environments/development.rb to:

Rails.application.config.middleware.use OmniAuth::Builder do
    provider :facebook, FACEBOOK_KEY, FACEBOOK_SECRET, {:client_options => {:ssl => {:ca_path => "/etc/ssl/certs"}}}
end

and then:

cd /etc/ssl/certs
sudo wget http://curl.haxx.se/ca/cacert.pem

wola!

Erosion answered 14/4, 2012 at 20:19 Comment(0)
C
0

I finally found a fix for Mountain Lion. See: http://coderwall.com/p/f4hyqw

rvm pkg install openssl
rvm reinstall 1.9.3 --with-openssl-dir=$rvm_path/usr
Cropper answered 22/8, 2012 at 6:53 Comment(0)
E
0

I encountered a similar error using RVM on Mountain Lion. It seems that Ruby can't find the CA certificate it needs to authorise the SSL connection. You need to install one. This solution did the trick:

http://fredwu.me/post/28834446907/fix-openssl-error-on-mountain-lion-and-rvm

(Although I couldn't actually load that page in my browser, I had to find it in the Google cache.)

Here's the short answer:

curl http://curl.haxx.se/ca/cacert.pem -o ~/.rvm/usr/ssl/cert.pem

And you're done.

Embark answered 13/9, 2012 at 21:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.