Josh McArthur

Secret Keeping with Rails

30 Sep 2012

Ever needed to store some secrets in Rails that you don’t want to share with the world? Yeah, same! In this post, I’m going to outline a really simple way to store your application’s secrets in a file called config/secrets.yml.

Secret image

Rails configuration and you

First of all, let’s discuss exactly how this is going to work. If you’ve even configured a Rails app before, you may have noticed that you’re setting your configuration on a config object inside a block that looks a bit like this:

MyRailsApp::Application.configure do |config|
  config.compile_assets = false
end

Something you might not know, though,is that these configuration settings are then available on an object called Rails.configuration. What we’re going to do is add an initializer that loads a secrets file, and then loads them into the Rails.configuration object so that we can access them in our application.

The advantage of this method versus something like Foreman is that:

  1. This method is much closer to Rails conventions - for example, instead of using a dotfile, it uses a basic initializer
  2. It doesn’t require running your application through any special interface like Foreman does
  3. It just does one thing, and does it well - storing configuration.

Reading the config file

As I’ve mentioned, we’ll be reading a file called config/secrets.yml. Let’s get our application set up to load this file.

NB: For this example, I’m assuming you’ve got a Rails 3.x application set up - you don’t need any models, controllers or anything else - we can test this from a rails console.

First of all, add the configuration file:

development:
  facebook:
    app_key: 123abc
    secret: 123abcdef

- as you can see, this secrets file just contains some credentials to use with Facebook OAuth - it can contain as many keys as you like, however.

Next we need to add a gem to our Gemfile. This gem is called Hashie, and extends Ruby’s Hash object to do some cool stuff - in our case, we can pass it a hash, and it will give us method access to our secrets - in other words, we can do this:

Rails.configuration.secrets.facebook.app_key

Instead of:

Rails.configuration.secrets['facebook']['app_key']

As a bonus, if you’re using Omniauth and any Omniauth strategies, you’ve probably already installed Hashie as a dependency of one of these - we’re just doing to re-use it here, so let’s add it to our Gemfile and be specific:

# Gemfile
# ...
gem 'hashie'

Next, let’s add an initializer to load this file in config/initializers/01_load_secrets. The “01” part is important; it tells Rails to load it before any other initializer runs (since we might need our secrets straight away in an initializer):

# config/initializers/01_load_secrets.rb
MyRailsApp::Application.configure do
  # Note: Hashie is a gem dependency
  config.secrets = Hashie::Mash.new(YAML.load_file(Rails.root.join('config', 'secrets.yml'))[Rails.env.to_s])
end 

I’ll just talk through what this is doing:

That’s all we need to do to load the config in - as you can see, it’s actually a really simple operation, and use of Hashie means that we don’t need to mess up our code later down the track with lots of square brackets and hash keys.

Using configuration

Once we’ve got the file loaded, actually using the configuration is actually really easy - just access your top-secret secrets on Rails.configuration.secrets.

For reference, here’s how you might set [Omniauth] to support logging in with Facebook using our new secrets.yml file:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :facebook,
  	Rails.configuration.secrets.facebook.app_key,
  	Rails.configuration.secrets.facebook.secret 
end
Referenced Projects