Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I authorize a Google Service Account without the default credentials file?

I have a Google Service Account that my app uses to retrieve data from Google Analytics.

When I created the account I downloaded a client_secrets file with all the necessary information for authorization via OAuth, and I recorded the path to this file in an environment variable called GOOGLE_APPLICATION_CREDENTIALS as per Google's documentation.

I can now get an authenticated client like this:

authorization = Google::Auth.get_application_default(scopes) 

This method reads the credentials out of the file, which works locally, but my app is hosted on Heroku where file storage is impossible.

The documentation states that I can either provide this file (can’t), run my app on an official Google Service (won’t), or experience an error.

How can I authenticate my service account without the client_secrets file?

like image 298
djb Avatar asked Mar 10 '16 23:03

djb


2 Answers

I found the answer in the source code of the google-auth-library-ruby gem.

It turns out that there is another option: take the values from the client_secrets file and put them in environment variables named GOOGLE_ACCOUNT_TYPE, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY respectively.

If these keys are populated, the credentials will load from there. Not a whisper of this in the docs, though.

like image 91
djb Avatar answered Nov 03 '22 11:11

djb


Since this is one of the main results that returns when searching google for "google service credentials ruby," I thought I would add my very recent experience to the list of possible answers.

Though you can do the method mentioned in the first answer, I found an alternate solution that works well with Heroku. I know it has been somewhat mentioned in another post, but the key thing that was left out was how to properly store the full GOOGLE_APPLICATION_CREDENTIALS .json file so that it can all be kept within one env on Heroku and not have special characters blow up your app when tryin to

I detail my steps below:

  1. Obtain your GOOGLE_APPLICATION_CREDENTIALS json file by following Google's instructions here: Getting Started with Authentication
  2. That will, of course, contain a json object with all the spaces, line returns, and quotations that heroku simply doesn't need. So, strip out all spaces and line breaks...AND PAY ATTENTION HERE -> EXCEPT FOR THE LINE BREAKS WITHIN THE 'BEGIN PRIVATE KEY' SEGMENT. Basically turn the json into one long string. Use whatever method you feel comfortable with.
  3. Once you have a single line json file with whitespace and line breaks removed, you will need to add it to Heroku by running the following command:

    heroku config:set GOOGLE_APPLICATION_CREDENTIALS="$(< /Users/whoever/Downloads/[CREDENTIAL_JSON_FILENAME].json)" --app your-app
    
  4. For my situation, I needed to have the service account available on initialization, so I placed this in an initializer in my rails app:

    GOOGLE_SERVICE_ACCOUNT_CREDENTIALS=Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: StringIO.new(ENV['GOOGLE_APPLICATION_CREDENTIALS'])
    )
    

Notice the StringIO.new() method. the #make_creds wants a file. So, fake it as such by using StringIO.new.

This method works perfectly.

If you need this to work differently on your local machine, you can always store the .json somewhere in the project and reference it through a file location string. Here is my full initializer:

require 'googleauth'

#https://www.rubydoc.info/github/google/google-auth-library-ruby/Google/Auth/ServiceAccountCredentials 
if Rails.env == "test"
  GOOGLE_SERVICE_ACCOUNT_CREDENTIALS = 
Google::Auth::ServiceAccountCredentials.make_creds(
    json_key_io: File.open('lib/google/google_application_credentials.json')
  ) 
elsif Rails.env != "development"
    GOOGLE_SERVICE_ACCOUNT_CREDENTIALS = 
Google::Auth::ServiceAccountCredentials.make_creds(
    json_key_io: StringIO.new(ENV['GOOGLE_APPLICATION_CREDENTIALS'])
  ) 
end

If you are using a gem like dotenv you can store the formatted json string as an ENV or you can just reference the file location in the ENV

I hope this helps someone.

like image 5
MiggityMac Avatar answered Nov 03 '22 11:11

MiggityMac