Copy to clipboard

Have you ever tried to setup Devise and Doorkeeper in the simplest possible way, without oauth applications etc? Here it is! In this article, I’ll show you in a few easy steps that setup you searched for!

Let’s start by creating new, simple Rails application (or clone this one https://github.com/Naturaily/devise-doorkeeper).

rails new myapp cd myapp rails g scaffold items name:string description:text rake db:migrate add root to: 'items#index' to config/routes.rb

Now we have a simple app with Items CRUD. Let’s add some code to handle Users.

add gem 'devise' to Gemfile and run bundle install rails g devise:install rails g devise User rake db:migrate add before_action :authenticate_user! to items_controller

OK, only logged in users can CRUD items now. Off to the most exciting part. We want the very same feature on the API, because mobile app is being created. We want the Items CRUD available and we will authenticate every action using Doorkeeper for this, because it’s the easiest thing you can do.

Let’s install Doorkeeper.

add gem 'doorkeeper' to Gemfile and run bundle install rails g doorkeeper:install rails g doorkeeper:migration

Now edit that new migration, it should look like this:

class CreateDoorkeeperTables < ActiveRecord :: Migration def change create_table :oauth_access_tokens do | t | t . integer :resource_owner_id t . integer :application_id t . string :token , null: false t . string :refresh_token t . integer :expires_in t . datetime :revoked_at t . datetime :created_at , null: false t . string :scopes end add_index :oauth_access_tokens , :token , unique: true add_index :oauth_access_tokens , :resource_owner_id add_index :oauth_access_tokens , :refresh_token , unique: true add_foreign_key ( :oauth_access_tokens , :users , column: :resource_owner_id ) end end

We removed oauth_applications and oauth_access_grants tables (we simply don’t need them). We need to remove associated foreing keys and indexes too. I also removed previous_refresh_token field from oauth_access_tokens table (please read the comment generated by Doorkeeper). And there is a little hack too. We need to change t.references :application, null: false to t.integer :application_id Without that our example won’t work!

Now we can run migrations

rake db :migrate

We need to mount doorkeeper in our router. It can be easily done by use_doorkeeper method. But we should rembember that we need nothing but tokens! So our code in config/routes.rb can looks like the code below:

use_doorkeeper do skip_controllers :authorizations , :applications , :authorized_applications end

Now let’s integrate Doorkeeper with Devise. First, we need a method to find user by email and password. Let’s edit app/models/user.rb .

class << self def authenticate ( email , password ) user = User . find_for_authentication ( email: email ) user . try ( :valid_password? , password ) ? user : nil end end

Next we configure Doorkeeper in config/initializers/doorkeeper.rb to use this method.

resource_owner_from_credentials do | _routes | User . authenticate ( params [ :email ], params [ :password ]) end

Don’t forget to let Doorkeeper access token with a password.

grant_flows %w(password)

We also want refresh tokens, so we need to uncomment the line with use_refresh_token .

Next, we skip app authorization.

skip_authorization do true end

There we go! We can now log in and log out to our API. Try this (please remember to keep the server launched):

curl -X POST -d "grant_type=password&email=user@example.com&password=yourpassword" localhost:3000/oauth/token

{ "access_token”:”YourAccessToken”,”token_type" : "bearer" , "expires_in" :7200, "refresh_token”:”YourRefreshToken”,”created_at" :1470946931 } %

DON’T FORGET TO USE SSL on production and staging environments!

OK, it’s time to use our tokens! Let’s retrieve some Items from our API. How? We need two new controllers. Why two? Because we should have separate controllers for API, so we need ItemsController and base controller for API.

We need app/controllers/api/base_controller.rb , a really simple one.

class Api::BaseController < ActionController :: API respond_to :json end

And app/controller/api/items_controller.rb (exemplary implementation).

class Api::ItemsController < Api :: BaseController before_action :doorkeeper_authorize! # equivalent of authenticate_user! def index @items = Item . all respond_with @items end # the rest of actions end

The most important part of code here is before_action :doorkeeper_authorize! . doorkeeper_authorize! is equaivalent of authenticate_user! . Without that every user could CRUD ours items.

The last one thing: add a new route

namespace :api do resources :items end

And that’s it! Let’s give it a try.

curl -v localhost:3000/api/items < HTTP/1.1 401 Unauthorized

curl -v localhost:3000/api/items?access_token = OurAccessTokenReturnedByAPI < HTTP/1.1 200 OK [{ "id" :1, "name" : "Item" , "description" : "Amazing Item." , "created_at" : "2016-08-11T19:25:09.649Z" , "updated_at" : "2016-08-11T19:25:09.649Z" }]

It works! Yay!

Is that all? Definitely no! We still don’t have registration via API. But there’s no need to describe it here, someone else has done that already. Please check this post.

Goodbye and good luck!