crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.33k stars 1.61k forks source link

SSL_connect error for oauth2 on heroku #5674

Closed MattMSumner closed 6 years ago

MattMSumner commented 6 years ago

When setting up oauth2 in a crystal app I could get everything running locally but upon deploying to heroku the oauth flow broke with:

SSL_connect: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed (OpenSSL::SSL::Error)

We fixed this by copying the OAuth2 code and modifying it to accept a HTTP::Client instead of building one. Then we make ourselves a client that points to heroku's SSL certs:

class GoogleAccessToken
  private getter client

  def initialize(@client : HTTP::Client)
  end

  def get_using_authorization_code(authorization_code)
    get_access_token do |form|
      form.add("redirect_uri", OAuthWebHook::Index.url)
      form.add("grant_type", "authorization_code")
      form.add("code", authorization_code)
    end
  end

  private def get_access_token
    body = HTTP::Params.build do |form|
      form.add("client_id", GoogleOAuth.settings.consumer_key)
      form.add("client_secret", GoogleOAuth.settings.consumer_secret)
      yield form
    end

    headers = HTTP::Headers{
      "Accept" => "application/json",
    }

    response = client.post(token_path, form: body, headers: headers)
    case response.status_code
    when 200, 201
      OAuth2::AccessToken.from_json(response.body)
    else
      raise OAuth2::Error.from_json(response.body)
    end
  end

  private def token_path
    GoogleOAuth::TOKEN_PATH
  end
end

And then we built a client like so:

  private def client
    HTTP::Client.new(host: HOST, tls: context)
  end

  private def context
    context = OpenSSL::SSL::Context::Client.insecure
    context.verify_mode = OpenSSL::SSL::VerifyMode::PEER
    setup_ssl_certificates(context)
    context
  end

  private def setup_ssl_certificates(context)
    Dir.glob(settings.certs_glob).each do |file|
      context.ca_certificates = file
    end
  end

So a possible solution could be to change OAuth2 to accept an option HTTP::Client.

MattMSumner commented 6 years ago

Example app that works for me locally but not when deployed to Heroku: https://github.com/MattMSumner/google-oauth2-example

And here it is deployed https://google-oauth-example.herokuapp.com

MattMSumner commented 6 years ago

Possibly a duplicate of https://github.com/crystal-lang/crystal/issues/5266

RX14 commented 6 years ago

Definitely a duplicate of #5266.