stalno u pokretu

< IT & tech

03. 09. 2014. api * back end * ruby * ruby gem * security

Devise as authentication solution for rails API

Instead of implementing my own authentication, I decided to adapt the Devise for my rails API. it differs in two main ways when applied to the rails API as opposed to the “regular” rails application:

  • it uses authentication token
  • it does not use sessions

Authentication token

Until recently Devise had the support for token authenticatable as an authentication method. That has changed since the Devise version 3.1. The reason given was a possible vulnerability to the timing attacks, since the token was being stored in the database undigested.

In short, timing attacks exploit the analyses of response times of methods that do a byte by byte comparison (for example of tokens) and deduce the progress of guessing the target. This can be addressed by providing a key along the token and by using a safe comparison method that either has random or constant execution time.

( -> read more about timing attacks in an article on Password attacks )

Simple Token Authentication gem that nicely wraps all the fixes for authentication token support for Devise was developed by Gonzalo Bulnes, and that is what I use, but I will also explain what it does every step of the way.


The use (and lack of) sessions

The sessions are used by Devise to “memorize” the signed in user and not force the authentication of every request. Since REST-full API should be stateless, the sessions are not used, and the authentication is required for each request for protected resources. However, there are still two types of requests:

  • one that can be seen as “sign in”, where username and password are sent in a request and an authentication token is received in a response, and
  • all other requests that contain that token and a database id (preferably not the table id, but rather some other unique value, such as email or UUID) in the params or in the header.

I will demonstrate how the sessions should be disabled in a moment.

Installation and initial setup

First, the gems should be added to the Gemfile:

Next, the generators should be run. I named my model “User”, but it can be something else.

The first generator creates the devise initializer with the default settings and the second one creates a model and configures it with the default Devise modules, configures the routes to the Devise controller and creates a migration.

The default URL options for the Devise mailer should be set in each environment:

Configuring the model

The migration needs to be updated to include other (custom) fields that might be needed for the specific application, and it needs to include the authentication_token.

Next, the Devise method should be used in the model to enable the modules that should be used. The validation of the user’s password, password_confirmation and email uniqueness is best left to the Devise.

By default, if the password_confirmation is nil (not sent at all, as opposed to being sent as empty), it does not affect the validity of a sign up. And that makes sense for an API, since the request is being sent by the client application (and not the user through a form), and that is where the check for confirmation should be done.

However the password_confirmation presence can be enforced as in the commented out line in the following gist:

A call to acts_as_token_authenticatable is a part of simple_token_authentication gem, and it ensures that if the authentication_token is nil, one is generated before saving.

It should be noted that by the design of the simple_token_authentication gem the authentication_token is not being digested before storing in the database. The reason stated by the gem author is that the security concern does not justify the overhead created by hashing the token on each request. As of the time of writing this article (early September 2014) an option to support hashing the token was not available, but there was plan to add it.

The fact that the generation is triggered when the token is nil can be used to easily reset the token when needed.

By default, when signing in the user the session is not being stored, but that can be changed in configuration.

The routes

In the routes file the devise_for needs to be set for the model that is being authenticated. Also the controllers that will be overridden need to have their default routes overridden as well. The default format should be json, and the scoping for api version is optional but can be handy.

Application controller

The application controller is inherited by other controllers, so it is a good place to authenticate user from the token. This is being done by acts_as_token_authentication_handler_for of the simple_authentication_token gem. fallback_to_devise option needs to be set to false, to prevent the CSRF attacks.

Custom Devise controllers

The task of registration controller is to sign up users (create their accounts), and sign them off (destroy / deactivate the accounts). The account creation does not require authentication for the account creation, so if the support for destroy action is not needed, all before filters from the following gist can be removed.

The original create method handles the sign up parameter validation, and that is still needed, and what should be overridden are the responses, that should be json, and should include success status (201) and any other information as specified for the application.

However, the destroy action needs to be authenticated, so the acts_as_token_authentication_handler_for method is used to add the authenticate_entity_from_token! and authenticate_entity! before filters. But these are needed only for destroy and need to be skipped for create.

In the destroy action the user can either be permanently destroyed (resource.destroy) or just deactivated. For deactivation to work, the deactivated_at field needs to be added to the migration, and taken into account when signing in.

Sessions controller is used in Devise authenticate the user by the credentials sent at the start of the session. In this setup for the API, there are no sessions, so the create action authenticates the user’s credentials and responds with the authentication_token that will be used for further authentication.

On create action, the authentication token can be reset. This is good security practice since the authentication_token is stored in the client application and could be compromised, but resetting the token will automatically “sign out” any client that was using it at that moment, and will prevent several clients to use it at the same time. This can be addressed by creating several tokens, one for each client.

Since there are no sessions, the sign out is not necessary, but can be used to manually reset the token.

If the module confirmable is enabled in the model, and the necessary fields are added in the migration, the confirmations controller can be customized. The show action receives the GET request with the (hashed) confirmation token in the params, and is responsible for confirming it. The only thing that needs to be overridden is the response.

I should be noted (especially for testing) that since the Devise version 3.1 the confirmation token is digested before storing in the database.

Testing the controllers

When testing the custom devise controllers with Rspec, it is necessary to include the Devise::TestHelpers and to set request.env[‘devise.mapping’] = Devise.mappings[:user] before each example. The reason for the later is that since the functional tests do not pass through the router, the Devise needs to be told explicitly what mapping is used.

In each request json needs to be specified as format, and this can be done like this: post :create, request_params.merge(format: ‘json’).

If the authentication token and the entity key are being sent via the header and not through the params (simple_token_authentication supports both out of the box), these helpers can be used in the test examples to add the token to the headers:

Other then that, testing the Devise custom controllers is just like testing any other controller.

And that is it, the authentication is set up, and the next step is dealing with the resource authorization, but that’s another story…

Related posts

01. 09. 2014. api * back end * bdd / tdd * cucumber * ruby * ruby gem
Developing rails REST API with Cucumber

json_spec, writing the definitions, checking the http headers, testing authentication...
[ reading time: ~ 3 min. ]

15. 08. 2014. api * back end * ruby
API - design, implementation, testing

notes and resources
[ reading time: ~ 3 min. ]

07. 09. 2014. api * back end * ruby
Building a JSON API in rails

[ reading time: ~ 1 min. ]

10. 08. 2014. api * back end
Representational state transfer (REST)

[ reading time: ~ 3 min. ]

17. 08. 2014. api * featured * security
Authentication techniques for REST API

API keys, username & password, authentication token, security concerns, nonce
[ reading time: ~ 5 min. ]

04. 09. 2014. api * back end
Design principles of the REST-ful JSON API (HATEOAS)

REST, JSON, API, HATEOAS, media types, top level resource representations, url templates, compound documents, urls, responses, errors, JSend response types, HTTP Status codes
[ reading time: ~ 12 min. ]

12. 09. 2014. back end * ruby
ActiveRecord notes

Uniqueness on a combination of fields, inclusion in array, getting all model associations, overriding model getters and setters, serialization...
[ reading time: ~ 2 min. ]

17. 09. 2014. back end * i18n&l10n * ruby
i18n gem advanced features – Ruby on Rails internationalization

Using Different Backends for Ruby i18n gem, chaining backends, caching is an i18n gem advanced feature, fallbacks, Translation Metadata, cascading lookups, translation symlinks, using custom exception handlers
[ reading time: ~ 14 min. ]

17. 09. 2014. back end * i18n&l10n * padrino * ruby * sinatra
Internationalization for Ruby – i18n gem

i18n for plain Ruby & the i18n gem, i18n for Ruby on Rails, i18n for Sinatra, i18n for Padrino
[ reading time: ~ 20 min. ]

17. 09. 2014. back end * i18n&l10n * ruby
Internationalization for Ruby with the r18n gem

Installation and setup, translation lookup, filters for translations processing, pluralization, translating activeRecord models and plain Ruby objects, locale settings, r18n wrappers for rails and sinatra, r18n vs i18n
[ reading time: ~ 9 min. ]

17. 09. 2014. back end * gettext * i18n&l10n * ruby
Ruby gettext internationalization tutorial on using the fast_gettext gem

Setup, translation lookup, managing translations (MO/PO, db, yaml), rails plugin, pluralization, defaults, multiple repositories, logging...
[ reading time: ~ 9 min. ]

22. 11. 2014. back end * deployment * ruby
rails deployment

Setting up Debian 7 server on VPS, nginx, php-fpm, ruby, unicorn/phusion passanger, essentials, ssh keys, capistrano...
[ reading time: ~ 2 min. ]

10. 12. 2014. back end * ruby gem

[ reading time: ~ 1 min. ]

05. 08. 2014. cucumber * ruby gem * bdd / tdd
Cucumber gem

[ reading time: ~ 1 min. ]

05. 08. 2014. bdd / tdd * ruby gem

[ reading time: ~ 1 min. ]

05. 08. 2014. bdd / tdd * rspec * ruby gem
Rspec gem

[ reading time: ~ 3 min. ]

07. 08. 2014. ruby gem
Devise gem

[ reading time: ~ 1 min. ]

18. 08. 2014. ruby gem * bdd / tdd
Forgery gem

[ reading time: ~ 1 min. ]

19. 08. 2014. security
Password attacks

Brute-force attack, dictionary attack, rainbow table attacks, timing attack
[ reading time: ~ 5 min. ]

21. 08. 2014. database * ruby
Adding UUID to rails

[ reading time: ~ 1 min. ]

30. 08. 2014. ruby gem * bdd / tdd

[ reading time: ~ 1 min. ]

17. 09. 2014. back end * cakephp * codeigniter * i18n&l10n * php * symfony * yii * zend
Internationalization How To for the 5 most popular PHP frameworks

In this how-To you will read about using CodeIgniter, CakePHP, Zend, Yii and Symfony
[ reading time: ~ 8 min. ]

17. 09. 2014. back end * fuelphp * i18n&l10n * laravel * php
PHP internationalization frameworks: Laravel and FuelPHP

The article covers two more PHP frameworks that are frequently used, Laravel and FuelPHP
[ reading time: ~ 6 min. ]

17. 09. 2014. back end * i18n&l10n * php
PHP internationalization – i18n mechanisms tutorial

Static web and internationalization, dynamic web applications, localizing strings directly in the code, storing the strings in a relational database, message catalogues (string arrays), JSON, use of resource files
[ reading time: ~ 10 min. ]

17. 09. 2014. back end * gettext * i18n&l10n * php
PHP internationalization with gettext tutorial

Installation, portable object template files, plurals, an example of a PO file, directory structure, machine object files, gettext caching problems, setting up PHP for internationalization with gettext...
[ reading time: ~ 18 min. ]

17. 09. 2014. back end * i18n&l10n
Resource file formats

ini files, properties files, iOS .strings files, YAML files, RESX and RESW files, RESJSON files
[ reading time: ~ 4 min. ]

17. 11. 2014. ruby gem * bdd / tdd

[ reading time: ~ 2 min. ]

24. 11. 2014. back end * dev_tool
nginx notes

[ reading time: ~ 1 min. ]

Ovaj sajt ne sadrži first party kukije i druge mehanizme za aktivno praćenje poseta/ponašanja (facebook, google analytics, itd...). Na nekim stranicama se nalaze embedovani youtube video klipovi i google mape koji učitavaju svoje kukije.