Authorization and Authentication in a Rails API

When creating an API in Rails your authentication (and to some extent authorization) techniques will vary somewhat based on your API’s purpose.

This article is part of a series on writing APIs in Rails. You can refer back to the original article for a list of the different types of APIs, links to other articles in the series, and background information.

Some Definitions

The terms authentication and authorization sometimes get mixed up when discussing application access control. It’s important to think about them as separate concerns when writing your application so here are some short definitions:

Authentication
Verification of the identity of the program or user making a request. Often uses something like a unique username and a password associated with that username.
Authorization
Once identity has been verified, allows/prevents programs or users from accessing certain part of the system or data stored in the system.

Web-Based Single Page Applications (SPA)

If your API is for a SPA it’s easiest to use Rails sessions to keep track of a user after they have authenticated. You won’t need any extra authentication/authorization provisions beyond what you would normally have for a page-oriented application.

Because everything is standard Rails it is easiest to use one of the popular authentication gems. Devise or Clearance authenticate a user with their username or email and a password. Alternatively, you can use Omniauth to authenticate based on credentials from another service like Facebook, Twitter, or Github.

After you have authenticated your user subsequent post requests to create/update data in your application should maintain Rails' CSRF protection mechanism.

Most of the JavaScript frameworks like Ember.js and AngularJS provide mechanisms for using Rails' CSRF mechanism.

Rails' jquery-ujs library also provides this for all XHR requests made via jQuery. It does this by setting HTTP_X_CSRF_TOKEN in the post request’s header. Just make sure you’re still including jquery-ujs via your asset pipeline.

Mobile and External Services

Because mobile device and other service applications typically don’t have the same cookies/session support as a browser you will need some other authorization scheme. Security is a very big consideration here so you’ll need to choose carefully.

The APIs available on existing popular services employ a variety of authentication schemes and there isn’t a common standard. Generally the simpler the scheme you choose the easier it will be to implement on the client and server, but also the less secure it will be.

Using a Simple Access Token

One of the simplest schemes is to use some sort of authorization token in a header or other part of the request. If you’re looking for a place to start you can create an API login endpoint and have it authenticate the user. You then create and store a token in the database and return it to the client application. Subsequent requests will be made with this token in the HTTP header data (preferred) or payload and will be used to look up the user for authorization purposes.

You’ll also need to consider token expiration policies. Simple implementations may use the same token for a single user on multiple devices or services. You can then move the token expiration forward with each request, effectively keeping the user logged in as long as they’re active. If you choose to set a fixed expiration users will effectively be logged out at the time the token expires.

You can also use a gem like Tiddle which combines with Devise to create and manage a unique token for each device or server your user logs in with.

More Sophisticated/Secure Schemes

If you need something more secure then OAuth is probably the most widely adopted standard. You can use the Doorkeeper gem to help with your implementation.

Forgery Protection

Regardless of how you authenticate a user for these types of APIs, you’ll need to turn off CSRF provisions on POST requests or otherwise you’ll get these entries in your log files:

Can't verify CSRF token authenticity

You can override the default forgery protection initially set in app/controllers/application_controller.rb:

protect_from_forgery with: :null_session

This will remove the server errors generated by the default setting that causes exceptions to be raised but it won’t get rid of the log messages.

You should be careful not to remove this check for server requests other than those directed at your API. If you have any admin pages or other browser-based forms for users to submit you should still perform this check on those POST requests.

You can skip the token authentication (leaving protect_from_forgery set to :exception) by adding this to the appropriate controller files:

skip_before_action :verify_authenticity_token

This method also takes an :if option where you can provide a predicate proc that determines if this request is for the API specifically, effectively shutting off CSRF checking.

Service Oriented Architectures

Depending on the network architecture of your SOA application, you may be able to run API endpoints without any authentication scheme at all if your services live behind a firewall.

If that isn’t the case then the methods mentioned previously for mobile and external services will also work. HTTP basic access authentication is another option and can be set up in your web server (e.g. Nginx/Apache) and not be a concern of your Rails application server. Client application servers will need to provide proper credentials when they make requests.

You’ll also need to consider where to authenticate any external users. It’s probably best to do that at the edge of your service in which case you may need to pass the user’s identity to other components in your service.

Authorization

Authorizing access to data in your system can be handled with the same techniques you use for page-oriented applications after authenticating the user’s credentials. Cancancan is a popular gem to do this but recently I’ve been leaning more towards Pundit because I find it a bit more transparent in how its rules are checked.

Next Up

Authentication and authorization may be one of the more complex issues you’ll need to tackle when implementing your API in a Rails application. There are several ways to accomplish this and a fair amount of discussion and debate around which ones are best.

Another heavily discussed topic is how to represent the data in your system. In the next article in this series I’ll start to talk about that with a discussion of how to define resources and build the URI routes for them.

comments powered by Disqus