4

I'm building an integration with the Google URL Shortening API (in Ruby). According to their instructions on authenticating with OAuth2 you need to define an environment variable GOOGLE_APPLICATION_CREDENTIALS that is the path to your JSON/P12 file with your service account credentials.

What I would like to know is how I can properly store my credentials without committing them to source control. I have the option of committing the JSON file with the credentials because the repo is private, but that just smells like bad practice.

DiegoSalazar
  • 13,361
  • 2
  • 38
  • 55
Daniel Bonnell
  • 4,817
  • 9
  • 48
  • 88

2 Answers2

4

I encountered this same problem when authorizing Google Calendar.

First, let me clarify / restate the problem since there seems to be some confusion (i.e. it's not as simple as "set environment variables").

Background

Google's Application Default Credentials expects you to set a an environment variable of GOOGLE_APPLICATION_CREDENTIALS which points to a JSON file containing your service account credentials.

Problem

Our standard solution of setting environment variables is not going to work with this pattern. We'd need to somehow reference environment variables inside a static JSON file. So how do we do that?

Solution

Turns out the maintainers of google-auth-library-ruby have already solved this problem. Reviewing the source code, you'll see that you can either:

  1. Set GOOGLE_APPLICATION_CREDENTIALS to point to a JSON file.
  2. Set three separate environment variables: GOOGLE_PRIVATE_KEY, GOOGLE_CLIENT_EMAIL, and GOOGLE_ACCOUNT_TYPE.

In option 2, if all three variables are present, google-auth-library-ruby will take care of the rest – no JSON file required.

Gotchas

  1. If you use option 2 above, make sure GOOGLE_APPLICATION_CREDENTIALS is not set. If GOOGLE_APPLICATION_CREDENTIALS is set, google-auth-library-ruby will favor it and option 2 will not work.
  2. GOOGLE_PRIVATE_KEY is a multi-line secret. Setting multi-line config vars can be tricky (at least on Heroku). For example, line breaks (i.e. \n) can get messed up due to double escaping. I had to follow these instructions from StackOverflow.
Community
  • 1
  • 1
Cory Schires
  • 2,146
  • 2
  • 14
  • 26
0

The first - and simplest - thing you can do is set environment variables. For example, I'm on Mac so I can open the file ~/.bash_profile and add export MY_VAR=something save and close it. Then, source the file to re-run it and execute the exports source ~/.bash_profile or simply open a new shell - that file is sourced automatically for new shell sessions.

Now you can access these in the command line like this echo $MY_VAR that will print something to the screen. In Ruby, you can access these through the ENV hash puts ENV["MY_VAR"].

Hosting providers have shortcuts to add these to your server, such as Heroku: heroku config:set MY_VAR=something.

As your env vars grow in number, you can use a gem called dotenv to keep them in a file and have them loaded automatically - read through their readme, they explain how to not commit it to your repo and have different credentials per enviroment in order to avoid pushing sensitive information.

DiegoSalazar
  • 13,361
  • 2
  • 38
  • 55
  • Thanks but that doesn't really answer my question at all. I already use dotenv to store credentials but so far as I'm aware you can't access an environment variable in a JSON file. The googleauth gem looks for the `GOOGLE_APPLICATION_CREDENTIALS` environment variable, which is a file path to a JSON file. I tried hosting the file on s3 but it won't accept a url. – Daniel Bonnell Dec 18 '15 at 20:04
  • I don't understand, it sounds like you're saying you're storing environment variables in a JSON file. You have to convert that to the the `export VAR=stuff` syntax for the shell to set them and be accessible from your code. The point is you can leave these out of your repo and set them manually on your server in order to keep creds private. – DiegoSalazar Dec 18 '15 at 20:08
  • I can set them manually, yes, but how to do *access* those variables from the JSON file? – Daniel Bonnell Dec 18 '15 at 20:09
  • Exactly how I've explained. Once you convert the variables from your json file to the `export MY_VAR=stuff` syntax into your `~/.bash_profile` and source it, you can access them from ruby with `ENV["MY_VAR"]`. They aren't environment variables until they've been `export`ed in the shell. – DiegoSalazar Dec 18 '15 at 20:11
  • But I don't need to access them from the Ruby app. Not from my code anyways. I already have them defined as ENV variables but that's not what I need help with. The gem only cares about the `GOOGLE_APPLICATION_CREDENTIALS` variable, which is a string representing the path to `client_secrets.json`, which is a manifest of OAuth credentials. Exporting the credentials as env variables isn't going to do me any good because when the gem reads the file from `GOOGLE_APPLICATION_CREDENTIALS` it tries to define the env variables, and would therefore overwrite any env variables I set beforehand. – Daniel Bonnell Dec 18 '15 at 20:16
  • Additionally, setting them after the fact won't help either because the gem will have already raised an exception if it can't get the credentials to generate an oauth token. – Daniel Bonnell Dec 18 '15 at 20:18
  • Ah, I see, you need to put the that file somewhere but not committed. If the gem doesn't support downloading the file from S3 you'd have to wrap it or add a rake task so ruby can copy it from S3 to your server. For example you write a rake task such as `rake get_client_secrets` that will download the file from S3 into the path at your `GOOGLE_APPLICATION_CREDENTIALS` and then the gem can access it. Or if that doesn't work because maybe the OAUTH gem needs that file at initialize time, you'd have to write a wrapper over the OAUTH gem and it can download the file before initializing the gem – DiegoSalazar Dec 18 '15 at 20:28
  • Well it's on Heroku so downloading the file isn't a good solution because it will be purged rather quickly. I know it's terrible practice but I think I'm just going commit the file to source. I can be almost certain the repo won't even be open-sourced and I can't come up with any other viable solution that can be quickly implemented. – Daniel Bonnell Dec 18 '15 at 20:38