1

My app is hosted on Heroku, so I'm trying to figure out how to use the JSON Google Cloud provides (to authenticate) as an environment variable, but so far I can't get authenticated.

I've searched Google and Stack Overflow and the best leads I found were:

Google Vision API authentication on heroku

How to upload a json file with secret keys to Heroku

Both say they were able to get it to work, but they don't provide code that I've been able to get work. Can someone please help me? I know it's probably something stupid.

I'm currently just trying to test the service in my product model leveraging this sample code from Google. Mine looks like this:

def self.google_vision_labels
  # Imports the Google Cloud client library
  require "google/cloud/vision"

  # Your Google Cloud Platform project ID
  project_id = "foo"

  # Instantiates a client
  vision = Google::Cloud::Vision.new project: project_id

  # The name of the image file to annotate
  file_name = "http://images5.fanpop.com/image/photos/27800000/FOOTBALL-god-sport-27863176-2272-1704.jpg"

  # Performs label detection on the image file
  labels = vision.image(file_name).labels

  puts "Labels:"
  labels.each do |label|
    puts label.description
  end
 end

I keep receiving this error,

RuntimeError: Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information

Based on what I've read, I tried placing the JSON contents in secrets.yml (I'm using the Figaro gem) and then referring to it in a Google.yml file based on the answer in this SO question.

In application.yml, I put (I overwrote some contents in this post for security):

GOOGLE_APPLICATION_CREDENTIALS: {
  "type": "service_account",
  "project_id": "my_project",
  "private_key_id": "2662293c6fca2f0ba784dca1b900acf51c59ee73",
  "private_key": "-----BEGIN PRIVATE KEY-----\n #keycontents \n-----END PRIVATE KEY-----\n",
  "client_email": "foo-labels@foo.iam.gserviceaccount.com",
  "client_id": "100",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": 
  "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": 
  "https://www.googleapis.com/robot/v1/metadata/x509/get-product-labels%40foo.iam.gserviceaccount.com"
}

and in config/google.yml, I put:

GOOGLE_APPLICATION_CREDENTIALS = ENV["GOOGLE_APPLICATION_CREDENTIALS"]

also, tried:

GOOGLE_APPLICATION_CREDENTIALS: ENV["GOOGLE_APPLICATION_CREDENTIALS"]

I have also tried changing these variable names in both files instead of GOOGLE_APPLICATION_CREDENTIALS with GOOGLE_CLOUD_KEYFILE_JSON and VISION_KEYFILE_JSON based on this Google page.

Can someone please, please help me understand what I'm doing wrong in referencing/creating the environmental variable with the JSON credentials? Thank you!

yellowreign
  • 3,528
  • 8
  • 43
  • 80
  • Im not 100% on this, Im not all that familiar with the figaro gem. I think its similar to the `dotenv` gem though. I know they can be used in a production environment (although I would recommend to use it only in dev/test environments) but if you are, it may be worth checking the Gemfile group the figaro gem has been added to matches the `RAILS_ENV` you are running the app on Heroku. Also, I see from the figaro docs it says the deployment process for Heroku should be a case of running a command like `figaro heroku:set -e production`. I hope that helps. – Dave K May 03 '18 at 22:51
  • Does your application work correctly if you launch it locally? Or do you have the same credential errors related to authentication? – Philipp Sh Jun 13 '18 at 11:55

4 Answers4

2

It's really annoying that Google decides to buck defacto credential standards by storing secrets via a file instead of a series of environment variables.

That said, my solution to this problem is to create a single .env variable GOOGLE_API_CREDS.

I paste the raw JSON blob into the .env then remove all newlines. Then in the application code I use JSON.parse(ENV.fetch('GOOGLE_API_CREDS') to convert the JSON blob into a real hash:

The .env file:

  GOOGLE_API_CREDS={"type": "service_account","project_id": "your_app_name", ... }

Then in the application code (Google OCR client as an example):

  Google::Cloud::Vision::ImageAnnotator.new(credentials: JSON.parse(ENV.fetch('GOOGLE_API_CREDS'))

Cheers

Dylan Pierce
  • 4,313
  • 3
  • 35
  • 45
0

Building on Dylan's answer, I found that I needed to use an extra line to configure the credentials as follows:

Google::Cloud::Language.configure {|gcl| gcl.credentials = JSON.parse(ENV['GOOGLE_APP_CREDS'])}

because the .new(credentials: ...) method was not working for Google::Cloud::Language

had to look in the (sparse) ruby reference section of Google Cloud Language.

And yeah... storing secrets in a file is quite annoying, indeed.

0

I had the same problem with Google Cloud Speech, using the "Getting Started" doc from Google.

The above answers helped a great deal, coupled with updating my Google Speech Gem to V1 (https://googleapis.dev/ruby/google-cloud-speech-v1/latest/Google/Cloud/Speech/V1/Speech/Client.html)

0

I simply use a StringIO object so that Psych thinks that it's an actual file that I read:

google:
  service: GCS
  project: ''
  bucket: ''
  credentials: <%= StringIO.new(ENV['GOOGLE_CREDENTIALS']) %>
Tallboy
  • 12,847
  • 13
  • 82
  • 173