Storing API credentials for a Rails app

Hey there!

If you’re just starting out with an API, you’ve probably tossed your API credentials somewhere as a class constant, like this:

class NokoClient PERSONAL_ACCESS_TOKEN = "abcd12345" URL = "https://api.letsfreckle.com/v2" include HTTParty def get_entries self . class . get ( " #{ URL } /entries" , :headers => { "X-NokoToken" => PERSONAL_ACCESS_TOKEN }) end end

This is completely valid for quickly hacking and trying things out. But once you’ve got a handle on APIs, you’ll want to start securely storing API keys for a few reasons:

Security: If your keys get checked into source control accidentally, they’re exposed and no longer a secret. Even if it’s a private repo, you’ll want to keep your keys closely guarded, since they’re the keys to the kingdom.

If your keys get checked into source control accidentally, they’re exposed and no longer a secret. Even if it’s a private repo, you’ll want to keep your keys closely guarded, since they’re the keys to the kingdom. Flexibility : Hard-coded values in a codebase are a pain to maintain. Let’s say that you need to regenerate your API keys. You’d have to dig through the code for every mention of the API key. It would be so much easier if your credentials were decoupled from your app and could be updated independently of the codebase.

: Hard-coded values in a codebase are a pain to maintain. Let’s say that you need to regenerate your API keys. You’d have to dig through the code for every mention of the API key. It would be so much easier if your credentials were decoupled from your app and could be updated independently of the codebase. Isolation : It’s standard practice to keep your production API keys away from development. You might use different accounts, the service might give you different keys, or you might have registered the same app for each environment (e.g: “MyApp (Development)”). In any case, the API keys always need to be separate and only load for the right environment.

: It’s standard practice to keep your production API keys away from development. You might use different accounts, the service might give you different keys, or you might have registered the same app for each environment (e.g: “MyApp (Development)”). Automated Deployment: As automated deployment becomes even more popular, you need a solution that seamlessly fits into your deployment scripts without mucking up the app’s repository with unrelated code.

The sanest approach that solves all these problems is to load the API credentials as environment variables in the app, which are stored in environment-specific YAML files. Thomas Fuchs introduced me to this pattern, and I’ve used it in all my apps since. The version below is for Rails 3+, let me know if you need a Rails 2.3 LTS version!

The code

# config/application.rb module YourApp class Application < Rails :: Application ... # Try loading a YAML file at `./config/env.[environment].yml`, if it exists # Kudos to Thomas Fuchs (http://mir.aculo.us) for the initial implementation def load_env_file ( environment = nil ) path = Rails . root . join ( "config" , "env #{ environment . nil? ? '' : '.' + environment } .yml" ) return unless File . exist? path config = YAML . load ( ERB . new ( File . new ( path ). read ). result ) config . each { | key , value | ENV [ key . to_s ] = value . to_s } end # Load environment variables. config/env.yml contains defaults which are # suitable for development. (This file is optional). load_env_file # Now look for custom environment variables, stored in env.[environment].yml # For development, this file is not checked into source control, so feel # free to tweak for your local development setup. Any values defined here # overwrite the defaults in `env.yml` load_env_file ( Rails . env ) end end

Now to add the YAML files store our credentials!

# config/env.yml # Just a test value to show overwriting a : " hello"

# config/env.development.yml # Just a test value to show overwriting a : " derp" freckle_personal_access_token : " abcd12345"

Now when we load the app, we’ll see these values loaded as environment variables!

$ rails c irb ( main ) :002:0> ENV[ 'a' ] => "derp" irb ( main ) :003:0> ENV[ 'freckle_personal_access_token' ] => "abcd12345"

Next up is to make sure the environment-specific YAML files are never stored in source control:

# .gitignore # Ignore the environment-specific YAML files /config/env. * .yml

Finally, we change our NokoClient class to use the new environment variable and we’re good to go!

class NokoClient URL = "https://api.letsfreckle.com/v2" include HTTParty def get_entries self . class . get ( " #{ URL } /entries" , :headers => { "X-NokoToken" => ENV [ 'freckle_personal_access_token' ] }) end end

Benefits

This approach has a number of benefits for development, security, and automated deployment:

Each developer can set their own environment variables for the specific features they work on.

A default set of environment variables exists with env.yml , which can even be used as an example configuration file with dummy values.

, which can even be used as an example configuration file with dummy values. Environment variables can be set as-needed and new environments can be painlessly setup. env.travis.yml could store all your environment variables for running tests on Travis.

could store all your environment variables for running tests on Travis. Automated deployment scripts can generate their own env.*.yml files for the target environment. The app will seamlessly use these new changes and the app’s repo is not mucked up with automated deployment code.

files for the target environment. The app will seamlessly use these new changes and the app’s repo is not mucked up with automated deployment code. Since production environment variables don’t need to be stored in the app’s repo, there’s less chance of an accidental commit.

Other Notes