This question already has answers here:
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
(37 answers)
Closed 8 years ago.
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?
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:
Faraday needs to be updated to support SSL ca_path. Install Faraday 0.6.1
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
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.
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
I am on ubuntu 10.10 (Maverick)... struggled about 6 hours before I got it to work, sharing my experience
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.
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
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.
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
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" } } }
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
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
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
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' })
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
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.
This seems to be a 1.9.x issue. Reverting to 1.8.7 fixed the issue.
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!
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
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!
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
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.
Related
I'm a noob studying Ruby on Rails. I am trying to integrate my ruby on rails app to Quickbooks. I'm following the steps of this video. And I am stuck when I try to authenticate by pressing the "Connect to QuickBooks" button.
I get this error;
I did a little research and found out that this error was about the URI.escape() command. I think they removed it in Ruby 3. How can I resolve this issue? I can't even figure out where this URI.escape() command is located.
This is from vendors_controller.rb
def authenticate
callback = oauth_callback_vendors_url
token = $qb_oauth_consumer.get_request_token(:oauth_callback => callback)
session[:qb_request_token] = token
# If Rails >= 4.1 you need to do this => session[:qb_request_token] = Marshal.dump(token)
redirect_to("https://appcenter.intuit.com/Connect/Begin?oauth_token=#{token.token}") and return
end
def oauth_callback
at = session[:qb_request_token].get_access_token(:oauth_verifier => params[:oauth_verifier])
# If Rails >= 4.1 you need to do this => at = Marshal.load(session[:qb_request_token]).get_access_token(:oauth_verifier => params[:oauth_verifier])
session[:token] = at.token
session[:secret] = at.secret
session[:realm_id] = params['realmId']
redirect_to root_url, notice: "Your QuickBooks account has been successfully linked."
end
My initializer quickbooks.rb
QB_KEY = "I PASTED MY CLIENT ID KEY HERE"
QB_SECRET = "I PASTED MY CLIENT SECRET HERE"
$qb_oauth_consumer = OAuth::Consumer.new(QB_KEY, QB_SECRET, {
:site => "https://oauth.intuit.com",
:request_token_path => "/oauth/v1/get_request_token",
:authorize_url => "https://appcenter.intuit.com/Connect/Begin",
:access_token_path => "/oauth/v1/get_access_token"
})
My rails version Rails 6.1.4
My ruby version ruby 3.0.1p64
Thank you for your time.
I know many people say that URI.escape wasn't good, but it was good enough for me when all I wanted from it is to escape urls with non-English characters.
So I just monkey patched it
Add the following under /config/initializers/functions_overrides.rb
(the file name can be anything you want)
require 'uri'
module URI
class << self
def escape(str)
alpha = "a-zA-Z"
alnum = "#{alpha}\\d"
unreserved = "\\-_.!~*'()#{alnum}"
reserved = ";/?:#&=+$,\\[\\]"
unsafe = Regexp.new("[^#{unreserved}#{reserved}]")
str.gsub(unsafe) do
us = $&
tmp = ''
us.each_byte do |uc|
tmp << sprintf('%%%02X', uc)
end
tmp
end.force_encoding(Encoding::US_ASCII)
end
end
end
Use the https://developer.intuit.com/app/developer/qbo/docs/develop/sdks-and-samples-collections/ruby
OAuth Ruby Client
Intuit offers an OAuth 2.0 Client which provides a set of methods that make it easier to work with Intuit’s OAuth and OpenID implementation.
Community Supported Ruby SDK
The Community Supported Ruby SDK makes it easy to integrate your web app with the QuickBooks Online API. This guide assumes that you have an existing web app that you want to integrate with QuickBooks Online.
URI#escape was deprecated and later removed - https://github.com/ruby/uri/commit/61c6a47ebf1f2726b60a2bbd70964d64e14b1f98
From the commit :
# This method is obsolete and should not be used. Instead, use
# CGI.escape, URI.encode_www_form or URI.encode_www_form_component
# depending on your specific use case.
Ruby 3.0.0 onwards URI#escape does not work.
Use CGI.escape or URI.encode_www_form as mentioned in the deprecation warning
I am new to RubyOnRails and SoundCloud.
I want to integrate SoundCloud API in my ruby on rails application.
For this I have registered on SoundCloud And I got the ClientID and ClientSecret. Also I have downloaded the SDK.
Now I have copied the files and folders from lib and spec directory to my applications lib and spec directory. Also I have added gem 'soundcloud' in the Gemfile.
After this I made simple code (copied from doc) in My Interactor:
# register a client with YOUR_CLIENT_ID as client_id_
client = SoundCloud.new(:client_id => YOUR_CLIENT_ID)
# get 10 hottest tracks
tracks = client.get('/tracks', :limit => 10, :order => 'hotness')
# print each link
tracks.each do |track|
puts track.permalink_url
end
But here I'm getting the error -
uninitialized constant MyApp::Interactors::MyInteractor::MyAction::SoundCloud
I followed the steps from APIDoc. Is there any step by step example for integrating SoundCloud in Ruby on Rails so that I can follow?
How can I resolve this error?
MyInteracor.rb
module MyApp
module Interactors
module MyInteractor
class MyAction < Struct.new(:user, :params)
def run
# SoundCloud
# register a client with YOUR_CLIENT_ID as client_id_
client = SoundCloud.new(:client_id => 'my-client-id')
# get 10 hottest tracks
tracks = client.get('/tracks', :limit => 10, :order => 'hotness')
# print each link
tracks.each do |track|
puts track.permalink_url
end
end
end
end
end
end
There's a typo in the soundcloud github page change the line:
client = SoundCloud.new(:client_id => 'my-client-id')
to
client = Soundcloud.new(:client_id => 'my-client-id')
[notice the lowercase c in Soundcloud]
Also you are going to need your client secret for SoundCloud's API to verify you.
Perhaps put client method and in it have client = SoundCloud.new(your-client-id,your-secret-key-your-redirect-uri) in a controller or helper with your client_id, client_secret, and redirect uri values protected in a .env file.
I think by leaving out your redirect_uri and client secret you might be getting this error in MyInteractor.rb
Hope this helps
Background:
Ruby 1.9.3
Rails 3.2.16
Windows 7 x64
Issue
I'm trying to fix the infamous
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
error by including my certificates in each HTTP request I perform. To do this I monkey patch Net::HTTP#use_ssl=:
# lib/gem_ext/net_http.rb
require 'open-uri'
require 'net/https'
module Net
class HTTP
alias_method :original_use_ssl=, :use_ssl=
def use_ssl=(flag)
store = OpenSSL::X509::Store.new
store.set_default_paths # Auto-include the system CAs.
Dir[Rails.root + 'config/certificates/*'].each do |cert|
puts "Adding cert: #{cert}"
store.add_cert(OpenSSL::X509::Certificate.new(File.read(cert)))
end
self.cert_store = store
self.verify_mode = OpenSSL::SSL::VERIFY_PEER
self.original_use_ssl = flag
end
end
end
Now, requests performed using Net::HTTP, for example:
> uri = URI.parse('https://internal-app/secure_url.json?foo=bar')
> Net::HTTP.start(uri.host, uri.port, :read_timeout => 10.minutes, :use_ssl => uri.scheme == 'https') do |http|
> http.request Net::HTTP::Get.new(uri.request_uri)
> end
Adding cert: config/certificates/cert1.cer
Adding cert: config/certificates/cert2.cer
=> #<Net::HTTPOK 200 OK readbody=true>
Work perfectly.
However, when i try to use OpenURI, which i thought was just a wrapper around Net::HTTP (and other IO operations), such as:
> require 'open-uri'
> open('https://our-all/secure_url.json?foo=bar', 'r', :read_timeout => 10.minutes)
Adding cert: config/certificates/cert1.cer
Adding cert: config/certificates/cert2.cer
#<Class:0x870e2e0>: SSL_connect returned=1 errno=0 state=SSLv3 read server certi
from D:/Ruby/Ruby193/lib/ruby/1.9.1/net/http.rb:800:in `connect'
from D:/Ruby/Ruby193/lib/ruby/1.9.1/net/http.rb:800:in `block in connect
So I can see my monkey patched method is getting hit ("Adding cert.."), but still I get the error. It seems as though something else is overriding it. Any ideas?
Thanks
I managed to come up with a solution (a while back now, so I hope it is still applicable).
According to the comments in my code, I needed to monkey patch cert_store= as well "as use_ssl= as Net::HTTP and OpenURI.open_httpcall these methods in different orders, so we need to ensure certificates are consistently added."
So, here is my solution:
module Net
class HTTP
alias_method :original_use_ssl=, :use_ssl=
def use_ssl=(flag)
store = OpenSSL::X509::Store.new
store.set_default_paths # Auto-include the system CAs.
self.cert_store = store # Now include internal certificates.
self.verify_mode = OpenSSL::SSL::VERIFY_PEER # Force verification.
self.original_use_ssl = flag
end
alias_method :original_cert_store=, :cert_store=
def cert_store=(store)
Dir[Rails.root + 'config/certificates/*'].each do |cert|
store.add_cert(OpenSSL::X509::Certificate.new(File.read(cert)))
end
self.original_cert_store = store
end
end
end
Hope that helps
I'm trying to turn httponly off for use in phonegap. I'm useing rails 3.1 and devise, each of which have reported (but not documented) ways of doing this, none of which work:
# application.rb
config.session_options = { :httponly => false } # no effect
config.session = { :httponly => false } # undefined method `session='
# devise.rb
config.cookie_options = { :httponly => false } # also no effect
to test I restarted the server, deleted the existing cookie, and reloaded the page. 'Http' column was still checked in the chrome debugger.
help!
This little snippet seems to work :
Testapp::Application.config.session_store :cookie_store, key: '_testapp_session', :domain => :all, :httponly => false
As far as I can tell, this is a bug in rails. Perhaps the option got removed, but the documentation stayed. Any ideas on this would be welcome!
I spent several thorough hours with ActionPack, and couln't find any reference to such a configuration option-- but I still don't have the full picture as to how it works. Specifically, there's the cookiestore which holdes cookies and writes them to the header (and is passed :httponly => true), but I couldn't find how the session is using the store-- with vague things like the Rails SessionManage module being a proverbial ghost town.
I hacked up a middleware which does the job:
# application.rb:
config.middleware.insert_before ActionDispatch::Cookies, "UnshieldCookie" # remove httponly.
# unshielded_cookie.rb
class UnshieldCookie
def initialize(app)
#app = app
end
def call(env)
status, headers, body = #app.call(env)
headers['Set-Cookie'].gsub!('HttpOnly', '') if headers['Set-Cookie'].present?
[status, headers, body]
end
end
I followed Ryan Bates Omniauth Part1 railscats http://railscasts.com/episodes/235-omniauth-part-1 . I put twitter and Facebook authentication with their secret numbers and when I try to authenticate through Facebook (auth/facebook) I get this error:
{
"error": {
"message": "Invalid redirect_uri: Given URL is not allowed by the Application configuration.",
"type": "OAuthException"
}
}
And when I try to authenticate through twitter (auth/twitter) I get this 401 Unauthorized response. I don't know how I can correct it
Thanks I corrected entering http://127.0.0.1:3000 in twitter URL callback field and in facebook my website field. But now when I try to authenticate with facebook I get this error:
OpenSSL::SSL::SSLError
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B:
certificate verify failed
How can I solve it? I solved putting OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE in development.rb
That error appears when your server runs on http protocol. You need to add this piece of code in your_project/script/rails before APP_PATH
require 'rubygems'
require 'rails/commands/server'
require 'rack'
require 'webrick'
require 'webrick/https'
module Rails
class Server < ::Rack::Server
def default_options
super.merge({
:Port => 3000,
:environment => (ENV['RAILS_ENV'] || "development").dup,
:daemonize => false,
:debugger => false,
:pid => File.expand_path("tmp/pids/server.pid"),
:config => File.expand_path("config.ru"),
:SSLEnable => true,
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
:SSLPrivateKey => OpenSSL::PKey::RSA.new(
File.open("/path_to_your/privatekey.pem").read),
:SSLCertificate => OpenSSL::X509::Certificate.new(
File.open("/path_to_your/servercert.crt").read),
:SSLCertName => [["CN", WEBrick::Utils::getservername]]
})
end
end
end
To generate self-signed certificates read this tutorial http://www.akadia.com/services/ssh_test_certificate.html (steps 1 to 4) or this www.tc.umn.edu/~brams006/selfsign.html
After updating your rails script change the url from http://127.0.0.1:3000 to https://127.0.0.1:3000
I get this problem fairly often with Twitter in development.
The issue is likely your callback url in your app settings. Try setting it to:
http://127.0.0.1
And try again. If it doesn't work from http://localhost:3000 then try it from http://127.0.0.1:3000
The problem with Facebook is also likely to be the callback URL in the app settings. For Facebook, my site url setting is: http://localhost:3000/