Shivam Daryanani's Blog

Creating a Secure and Versioned API in Rails

Been working on a side project with a friend and we were at the point where we needed to build an API for the code we had written. The idea was to build an API which can be accessed by our customers with an API key.

After some research, I found couple of gems that make it easy to do this. Rocket Pants and Versionist. In order to reduce the dependencies, I decided to build it myself with the help of RailsCasts.

Creating the API.

We will start off by creating the routes for it.

config/routes.rb
1
2
3
4
5
6
7
namespace :api do
  namespace :v1 do
     namespace :search do
       get "youtube"
     end
  end
end

This block of code gives us the following route:

1
/api/v1/search/youtube

Now in order to create the controller, since they are namespaced under api and the version number you must put the controller inside the two modules.

app/controllers/api/v1/search_controller.rb
1
2
3
4
5
6
7
8
9
10
11
module Api
  module V1
    class SearchController < ApplicationController

      def youtube
        #code that searches for youtube videos
      end

    end
  end
end

Now doing the following should give you a list of YouTube results in a JSON format.

1
curl yoururl.com/api/v1/search/youtube?query=funny

This setup gives us the ability to easily add new versions of the API while maintaining backwards compatibility. The only thing is that the API is not secure.

Securing the API.

There are a few ways you can do this. Instead of doing it with OAuth I just created an access token given to each application created. The access token is created using a SecureRandom.hex string.

app/models/user.rb
1
2
3
4
5
class User < ActiveRecord::Base
  #code for authentication 

  has_many :applications
end
app/models/application.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
class Application < ActiveRecord::Base
  before_create :generate_access_token

  belongs_to :user

  private

  def generate_access_token
    begin
      self.access_token = SecureRandom.hex
    end while self.class.exists?(access_token: access_token)
  end
end

The way I set this up is that a user can have many applications, and each application belongs to a user. Before an application is created there is a unique key which is generated restricing access to the API. There are other columns you can add to the model if needed such as permissions, expires_at etc.

app/controllers/api/v1/search_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module Api
  module V1
    class SearchController < ApplicationController
      before_filter :restrict_access

      def youtube
        #code that searches for youtube videos
      end

      private

      def restrict_access
        app = Application.find_by_access_token(params[:access_token])
        head :unauthorized unless app
      end

    end
  end
end

Updating the controller in order to send an unauthorized response if the access key is invalid. Now in order to make a request the user has to send in the right access_token in the parameters. Also make sure to not make the access_token field accessible using attr_accessible or in rails 4 you can do the following:

app/controllers/applications.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ApplicationsController < ApplicationController
  #code for controller

  def create
    @application = current_user.applications.build(application_params)
    #code to save application
  end

  private

  def application_params
    params.require(:application).permit(:name)
  end

end

Instead of directly passing in the params[:application] we pass the build method a method itself. This private method makes sure to only permit the application name.

Since the output of my controllers were already in JSON there was no need for RABL. Also for those looking for something lightweight can have a look at Rails-API. This is enough to get you started if you any questions leave comments and I shall address them.

Comments