Looking For Suggestions For Building a Secure Rest API Within Ruby on Rails

Looking for suggestions for building a secure REST API within Ruby on Rails

There are several schemes for authenticating API requests, and they're different than normal authentication provided by plugins like restful_authentication or acts_as_authenticated. Most importantly, clients will not be maintaining sessions, so there's no concept of a login.

HTTP Authentication

You can use basic HTTP authentication. For this, API clients will use a regular username and password and just put it in the URL like so:

http://myusername:mypass@www.someapp.com/

I believe that restful_authentication supports this out of the box, so you can ignore whether or not someone is using your app via the API or via a browser.

One downside here is that you're asking users to put their username and password in the clear in every request. By doing it over SSL, you can make this safe.

I don't think I've ever actually seen an API that uses this, though. It seems like a decently good idea to me, especially since it's supported out of the box by the current authentication schemes, so I don't know what the problem is.

API Key

Another easy way to enable API authentication is to use API keys. It's essentially a username for a remote service. When someone signs up to use your API, you give them an API key. This needs to be passed with each request.

One downside here is that if anyone gets someone else's API key, they can make requests as that user. I think that by making all your API requests use HTTPS (SSL), you can offset this risk somewhat.

Another downside is that users use the same authentication credentials (the API key) everywhere they go. If they want to revoke access to an API client their only option is to change their API key, which will disable all other clients as well. This can be mitigated by allowing users to generate multiple API keys.

API Key + Secret Key signing

Deprecated(sort of) - see OAuth below

Significantly more complex is signing the request with a secret key. This is what Amazon Web Services (S3, EC2, and such do). Essentially, you give the user 2 keys: their API key (ie. username) and their secret key (ie. password). The API key is transmitted with each request, but the secret key is not. Instead, it is used to sign each request, usually by adding another parameter.

IIRC, Amazon accomplishes this by taking all the parameters to the request, and ordering them by parameter name. Then, this string is hashed, using the user's secret key as the hash key. This new value is appended as a new parameter to the request prior to being sent. On Amazon's side, they do the same thing. They take all parameters (except the signature), order them, and hash using the secret key. If this matches the signature, they know the request is legitimate.

The downside here is complexity. Getting this scheme to work correctly is a pain, both for the API developer and the clients. Expect lots of support calls and angry emails from client developers who can't get things to work.

OAuth

To combat some of the complexity issues with key + secret signing, a standard has emerged called OAuth. At the core OAuth is a flavor of key + secret signing, but much of it is standardized and has been included into libraries for many languages.

In general, it's much easier on both the API producer and consumer to use OAuth rather than creating your own key/signature system.

OAuth also inherently segments access, providing different access credentials for each API consumer. This allows users to selectively revoke access without affecting their other consuming applications.

Specifically for Ruby, there is an OAuth gem that provides support out of the box for both producers and consumers of OAuth. I have used this gem to build an API and also to consume OAuth APIs and was very impressed. If you think your application needs OAuth (as opposed to the simpler API key scheme), then I can easily recommend using the OAuth gem.

How to secure access to rails server which provides REST API access

Well, it depends of your app architecture.
You can use devise to authenticate users at REST API.
But if your Service App is for internal use only, for example it provides data only for another app, you can restrict access by ip, or Basic HTTP auth.

My opinion, that devise is good only for authorising end-users, but not services.

Building an API with/without OAuth and OpenID

Devise is a fantastic gem that handles authentication in rails apps. It also provides token based authentication. You can find many resources on the web (for example here) explainig how to use it. No doubt it will fit for your situation.

How do you authenticate user generated apps for your app?

This may be what I need:

http://github.com/phurni/authlogic_api

Rails 3. How to add an API to a rails app

a good starting point might be reading up on REST and responders

Then to interact with the API from another rails app, you can use ActiveResource. There's a Railscast on it.

An example:

#API side
class ProductsController < ApplicationController
respond_to :json

def index
@products = Product.all
respond_with(@products)
end
end

#Client
# models/product.rb
class Product < ActiveResource::Base
self.site = "http://your-api-app.com"
end

What can I use to allow an android app to communicate with a rails app

Here is a fast walkthrough.

Preparing the base application

Let's create a new rails app

$ rails new simple-message
$ cd simple-message/

We are now going to create a RESTful resource called Message, that will manage the messages coming from your mobiles.

$ rails generate scaffold Message title:string content:text

Create the table that will contain the messages:

$ rake db:migrate

Start the boundled server and point your browser to http://0.0.0.0:3000/messages

From there you can manually create new messages, that will show up as a list.

Your API is already there

For every page you can navigate with your browser there is a corresponding view that accepts programmatic calls from other clients.

If you browse http://0.0.0.0:3000/messages.xml you will get an xml containing all you messages. With curl:

$ curl http://localhost:3000/messages.xml
<?xml version="1.0" encoding="UTF-8"?>
<messages type="array">
<message>
<created-at type="datetime">2010-11-27T18:24:36Z</created-at>
<title>Just a message</title>
<updated-at type="datetime">2010-11-27T18:24:36Z</updated-at>
<id type="integer">1</id>
<content>With some content</content>
</message>
</messages>

Its time to add a new message:

$ curl -X POST -H 'Content-type: text/xml' -d '<message><title>Hello</title><content>How are you</content></message>' http://0.0.0.0:3000/messages.xml
<?xml version="1.0" encoding="UTF-8"?>
<message>
<created-at type="datetime">2010-11-27T18:40:44Z</created-at>
<title>Hello</title>
<updated-at type="datetime">2010-11-27T18:40:44Z</updated-at>
<id type="integer">2</id>
<content>How are you</content>
</message>

Your Android client will have to act as curl to add new messages.

Securing your web app

Now you need some authentication to allow only certain user to act as an admin and to restric the use of your API. Before digging for the various way to implement authentication in Rails:

  • should your user authenticate before posting a message?
  • should the web app accept authentication from other services (i.e. Twitter, Facebook, Google, OAuth, OpenID...) or against your own user database?
  • are your API open to other clients, or just your Android client can post messages?
  • do you need to know who posted the message (i.e. the user login)?
  • do you want to block a single user or an application to post messages to your web app?

You can find a nice recap of different strategies here.

Where to put API keys for Twitter app on server

A common approach is to save the environment variables into a file called .env that is ignored by version control (and therefore won't be included on Github) but read by the code. One gem to help with this is dotenv.

  1. add .env to the .gitignore file.
  2. create a local .env file with all your env vars
  3. require 'dotenv' and put Dotenv.load somewhere at the beginning of your script. In Rails, the require is unnecessary and you can place the load call in any file in the config/initializers folder
  4. Check that your app works fine locally. The environment variables should be found in the ENV hash from Ruby code.
  5. Save changes and push new version of app to digital ocean
  6. manually create the .env file on the digital ocean server, in the root of the repo
  7. run digital ocean server and check that everything works.

other notes:

  • see How To Read and Set Environmental and Shell Variables on a Linux VPS

  • some platforms like heroku have a different mechanism for setting environment variables, such as heroku config:set or web UIs.

  • You can set environment variables on a one-off basis using the env command in bash, for example:

    env a=hello b=' world' ruby -e 'puts ENV["a"] + ENV["b"]'
    # => hello world

    This can give a quick way to configure a program without getting into argument parsing. For example in Rails, you can say rails c test to open a console using the test environment, but env RAILS_ENV=test rails c should do the same thing.

Is it okay to use two different API's on the same project for a different activities?

There is no definitive answer, but in general it is considered bad practice to use multiple API frameworks within the same project. This is because each framework has its own set of conventions and best practices, which can lead to confusion and inconsistency. It is usually best to choose one framework and stick with it.



Related Topics



Leave a reply



Submit